For TLS QUIC handshakes, reduce the GFE<=>Alia round trip down to 1 using BoringSSL's handshake hints. This change includes the following pieces:
- Send ssl_capabilities and ALPS from GFE to Alia, and send handshake_hints from Alia to GFE.
- Change QUIC's TlsServerHandshaker to set ALPS earlier, and set handshake hints if present.
- Change Alia's TLSHandshakeV2 handler to generate handshake hints.
- Change SelectCertRequest's next_protocol from a repeated string to repeated ProtocolInfo, each ProtocolInfo contains a protocol name and an optional ALPS.

See http://go/handshake-hints for the high level design.

Tested:
- Update standalone_handshaker_end_to_end_test.cc to verify that when handshake hints are enabled, GFE only talks to Alia once for both 1RTT and 0RTT handshakes.
- Update tls_handshake_v2_test.cc to use a LetoCrypter on the edge server, and verify the ticket can be decrypted by Alia.

Protected by FLAGS_quic_reloadable_flag_quic_tls_server_use_handshake_hints.

PiperOrigin-RevId: 371340778
Change-Id: I1b70c37b57c5e3d365628c236da8cbfdc81ea07a
diff --git a/quic/core/crypto/crypto_utils.cc b/quic/core/crypto/crypto_utils.cc
index 2c84350..67527d4 100644
--- a/quic/core/crypto/crypto_utils.cc
+++ b/quic/core/crypto/crypto_utils.cc
@@ -762,4 +762,21 @@
 }
 
 #undef RETURN_STRING_LITERAL  // undef for jumbo builds
+
+// static
+bool CryptoUtils::GetSSLCapabilities(const SSL* ssl,
+                                     bssl::UniquePtr<uint8_t>* capabilities,
+                                     size_t* capabilities_len) {
+  uint8_t* buffer;
+  CBB cbb;
+
+  if (!CBB_init(&cbb, 128) || !SSL_serialize_capabilities(ssl, &cbb) ||
+      !CBB_finish(&cbb, &buffer, capabilities_len)) {
+    return false;
+  }
+
+  *capabilities = bssl::UniquePtr<uint8_t>(buffer);
+  return true;
+}
+
 }  // namespace quic
diff --git a/quic/core/crypto/crypto_utils.h b/quic/core/crypto/crypto_utils.h
index ec68c65..7aa32e1 100644
--- a/quic/core/crypto/crypto_utils.h
+++ b/quic/core/crypto/crypto_utils.h
@@ -237,6 +237,11 @@
   // Returns a hash of the serialized |message|.
   static std::string HashHandshakeMessage(const CryptoHandshakeMessage& message,
                                           Perspective perspective);
+
+  // Wraps SSL_serialize_capabilities. Return nullptr if failed.
+  static bool GetSSLCapabilities(const SSL* ssl,
+                                 bssl::UniquePtr<uint8_t>* capabilities,
+                                 size_t* capabilities_len);
 };
 
 }  // namespace quic
diff --git a/quic/core/crypto/proof_source.h b/quic/core/crypto/proof_source.h
index cee94f2..31cbfd6 100644
--- a/quic/core/crypto/proof_source.h
+++ b/quic/core/crypto/proof_source.h
@@ -229,12 +229,15 @@
   //      whether it is completed before ProofSourceHandle::SelectCertificate
   //      returned.
   // |chain| the certificate chain in leaf-first order.
+  // |handshake_hints| (optional) handshake hints that can be used by
+  //      SSL_set_handshake_hints.
   //
   // When called asynchronously(is_sync=false), this method will be responsible
   // to continue the handshake from where it left off.
   virtual void OnSelectCertificateDone(bool ok,
                                        bool is_sync,
-                                       const ProofSource::Chain* chain) = 0;
+                                       const ProofSource::Chain* chain,
+                                       absl::string_view handshake_hints) = 0;
 
   // Called when a ProofSourceHandle::ComputeSignature operation completes.
   virtual void OnComputeSignatureDone(
@@ -280,9 +283,11 @@
   virtual QuicAsyncStatus SelectCertificate(
       const QuicSocketAddress& server_address,
       const QuicSocketAddress& client_address,
+      absl::string_view ssl_capabilities,
       const std::string& hostname,
       absl::string_view client_hello,
       const std::string& alpn,
+      absl::optional<std::string> alps,
       const std::vector<uint8_t>& quic_transport_params,
       const absl::optional<std::vector<uint8_t>>& early_data_context) = 0;
 
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 8b27a6a..536f139 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -63,6 +63,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_start_peer_migration_earlier, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_false, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_true, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_server_use_handshake_hints, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_connection_id_on_default_path_v2, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_encryption_level_context, true)
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index 19642fe..b2424a5 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -55,9 +55,11 @@
 TlsServerHandshaker::DefaultProofSourceHandle::SelectCertificate(
     const QuicSocketAddress& server_address,
     const QuicSocketAddress& client_address,
+    absl::string_view /*ssl_capabilities*/,
     const std::string& hostname,
     absl::string_view /*client_hello*/,
     const std::string& /*alpn*/,
+    absl::optional<std::string> /*alps*/,
     const std::vector<uint8_t>& /*quic_transport_params*/,
     const absl::optional<std::vector<uint8_t>>& /*early_data_context*/) {
   if (!handshaker_ || !proof_source_) {
@@ -70,7 +72,8 @@
       proof_source_->GetCertChain(server_address, client_address, hostname);
 
   handshaker_->OnSelectCertificateDone(
-      /*ok=*/true, /*is_sync=*/true, chain.get());
+      /*ok=*/true, /*is_sync=*/true, chain.get(),
+      /*handshake_hints=*/absl::string_view());
   if (!handshaker_->select_cert_status().has_value()) {
     QUIC_BUG(quic_bug_12423_1)
         << "select_cert_status() has no value after a synchronous select cert";
@@ -811,13 +814,40 @@
     return ssl_select_cert_error;
   }
 
+  bssl::UniquePtr<uint8_t> ssl_capabilities;
+  size_t ssl_capabilities_len = 0;
+  absl::string_view ssl_capabilities_view;
+
+  absl::optional<std::string> alps;
+  if (use_handshake_hints_) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_tls_server_use_handshake_hints);
+    if (CryptoUtils::GetSSLCapabilities(ssl(), &ssl_capabilities,
+                                        &ssl_capabilities_len)) {
+      ssl_capabilities_view = absl::string_view(
+          reinterpret_cast<const char*>(ssl_capabilities.get()),
+          ssl_capabilities_len);
+    }
+
+    // Enable ALPS for the session's ALPN.
+    SetApplicationSettingsResult alps_result =
+        SetApplicationSettings(AlpnForVersion(session()->version()));
+    if (!alps_result.success) {
+      return ssl_select_cert_error;
+    }
+    alps = alps_result.alps_length > 0
+               ? std::string(alps_result.alps_buffer.get(),
+                             alps_result.alps_length)
+               : std::string();
+  }
+
   const QuicAsyncStatus status = proof_source_handle_->SelectCertificate(
       session()->connection()->self_address(),
-      session()->connection()->peer_address(), crypto_negotiated_params_->sni,
+      session()->connection()->peer_address(), ssl_capabilities_view,
+      crypto_negotiated_params_->sni,
       absl::string_view(
           reinterpret_cast<const char*>(client_hello->client_hello),
           client_hello->client_hello_len),
-      AlpnForVersion(session()->version()),
+      AlpnForVersion(session()->version()), std::move(alps),
       set_transport_params_result.quic_transport_params,
       set_transport_params_result.early_data_context);
 
@@ -843,13 +873,27 @@
 void TlsServerHandshaker::OnSelectCertificateDone(
     bool ok,
     bool is_sync,
-    const ProofSource::Chain* chain) {
+    const ProofSource::Chain* chain,
+    absl::string_view handshake_hints) {
   QUIC_DVLOG(1) << "OnSelectCertificateDone. ok:" << ok
-                << ", is_sync:" << is_sync;
+                << ", is_sync:" << is_sync
+                << ", len(handshake_hints):" << handshake_hints.size();
   select_cert_status_ = QUIC_FAILURE;
   if (ok) {
     if (chain && !chain->certs.empty()) {
       tls_connection_.SetCertChain(chain->ToCryptoBuffers().value);
+      if (use_handshake_hints_) {
+        if (!handshake_hints.empty() &&
+            !SSL_set_handshake_hints(
+                ssl(), reinterpret_cast<const uint8_t*>(handshake_hints.data()),
+                handshake_hints.size())) {
+          // If |SSL_set_handshake_hints| fails, the ssl() object will remain
+          // intact, it is as if we didn't call it. The handshaker will
+          // continue to compute signature/decrypt ticket as normal.
+          QUIC_CODE_COUNT(quic_tls_server_set_handshake_hints_failed);
+          QUIC_DVLOG(1) << "SSL_set_handshake_hints failed";
+        }
+      }
       select_cert_status_ = QUIC_SUCCESS;
     } else {
       QUIC_LOG(ERROR) << "No certs provided for host '" << hostname_ << "'";
@@ -927,16 +971,32 @@
                        alpn_length);
   }
 
+  // TODO(wub): Remove QuicSession::SelectAlpn. QuicSessions should know the
+  // ALPN on construction.
   auto selected_alpn = session()->SelectAlpn(alpns);
   if (selected_alpn == alpns.end()) {
     QUIC_DLOG(ERROR) << "No known ALPN provided by client";
     return SSL_TLSEXT_ERR_NOACK;
   }
 
-  // Enable ALPS for the selected ALPN protocol.
+  if (!use_handshake_hints_) {
+    // Enable ALPS for the selected ALPN protocol.
+    if (!SetApplicationSettings(*selected_alpn).success) {
+      return SSL_TLSEXT_ERR_NOACK;
+    }
+  }
+
+  session()->OnAlpnSelected(*selected_alpn);
+  valid_alpn_received_ = true;
+  *out_len = selected_alpn->size();
+  *out = reinterpret_cast<const uint8_t*>(selected_alpn->data());
+  return SSL_TLSEXT_ERR_OK;
+}
+
+TlsServerHandshaker::SetApplicationSettingsResult
+TlsServerHandshaker::SetApplicationSettings(absl::string_view alpn) {
+  TlsServerHandshaker::SetApplicationSettingsResult result;
   const uint8_t* alps_data = nullptr;
-  size_t alps_length = 0;
-  std::unique_ptr<char[]> buffer;
 
   const std::string& hostname = crypto_negotiated_params_->sni;
   std::string accept_ch_value = GetAcceptChValueForOrigin(hostname);
@@ -950,22 +1010,20 @@
 
   if (!accept_ch_value.empty()) {
     AcceptChFrame frame{{{std::move(origin), std::move(accept_ch_value)}}};
-    alps_length = HttpEncoder::SerializeAcceptChFrame(frame, &buffer);
-    alps_data = reinterpret_cast<const uint8_t*>(buffer.get());
+    result.alps_length =
+        HttpEncoder::SerializeAcceptChFrame(frame, &result.alps_buffer);
+    alps_data = reinterpret_cast<const uint8_t*>(result.alps_buffer.get());
   }
 
   if (SSL_add_application_settings(
-          ssl(), reinterpret_cast<const uint8_t*>(selected_alpn->data()),
-          selected_alpn->size(), alps_data, alps_length) != 1) {
+          ssl(), reinterpret_cast<const uint8_t*>(alpn.data()), alpn.size(),
+          alps_data, result.alps_length) != 1) {
     QUIC_DLOG(ERROR) << "Failed to enable ALPS";
-    return SSL_TLSEXT_ERR_NOACK;
+    result.success = false;
+  } else {
+    result.success = true;
   }
-
-  session()->OnAlpnSelected(*selected_alpn);
-  valid_alpn_received_ = true;
-  *out_len = selected_alpn->size();
-  *out = reinterpret_cast<const uint8_t*>(selected_alpn->data());
-  return SSL_TLSEXT_ERR_OK;
+  return result;
 }
 
 }  // namespace quic
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h
index b853462..93f231d 100644
--- a/quic/core/tls_server_handshaker.h
+++ b/quic/core/tls_server_handshaker.h
@@ -19,6 +19,7 @@
 #include "quic/core/quic_types.h"
 #include "quic/core/tls_handshaker.h"
 #include "quic/platform/api/quic_export.h"
+#include "quic/platform/api/quic_flags.h"
 
 namespace quic {
 
@@ -166,7 +167,8 @@
   // ProofSourceHandleCallback implementation:
   void OnSelectCertificateDone(bool ok,
                                bool is_sync,
-                               const ProofSource::Chain* chain) override;
+                               const ProofSource::Chain* chain,
+                               absl::string_view handshake_hints) override;
 
   void OnComputeSignatureDone(
       bool ok,
@@ -206,9 +208,11 @@
     QuicAsyncStatus SelectCertificate(
         const QuicSocketAddress& server_address,
         const QuicSocketAddress& client_address,
+        absl::string_view ssl_capabilities,
         const std::string& hostname,
         absl::string_view client_hello,
         const std::string& alpn,
+        absl::optional<std::string> alps,
         const std::vector<uint8_t>& quic_transport_params,
         const absl::optional<std::vector<uint8_t>>& early_data_context)
         override;
@@ -276,6 +280,13 @@
   bool ProcessTransportParameters(const SSL_CLIENT_HELLO* client_hello,
                                   std::string* error_details);
 
+  struct QUIC_NO_EXPORT SetApplicationSettingsResult {
+    bool success = false;
+    std::unique_ptr<char[]> alps_buffer;
+    size_t alps_length = 0;
+  };
+  SetApplicationSettingsResult SetApplicationSettings(absl::string_view alpn);
+
   QuicConnectionStats& connection_stats() {
     return session()->connection()->mutable_stats();
   }
@@ -320,6 +331,8 @@
       crypto_negotiated_params_;
   TlsServerConnection tls_connection_;
   const QuicCryptoServerConfig* crypto_config_;  // Unowned.
+  const bool use_handshake_hints_ =
+      GetQuicReloadableFlag(quic_tls_server_use_handshake_hints);
 };
 
 }  // namespace quic
diff --git a/quic/test_tools/fake_proof_source_handle.cc b/quic/test_tools/fake_proof_source_handle.cc
index b239f7d..b1fedf8 100644
--- a/quic/test_tools/fake_proof_source_handle.cc
+++ b/quic/test_tools/fake_proof_source_handle.cc
@@ -71,14 +71,16 @@
 QuicAsyncStatus FakeProofSourceHandle::SelectCertificate(
     const QuicSocketAddress& server_address,
     const QuicSocketAddress& client_address,
+    absl::string_view ssl_capabilities,
     const std::string& hostname,
     absl::string_view client_hello,
     const std::string& alpn,
+    absl::optional<std::string> alps,
     const std::vector<uint8_t>& quic_transport_params,
     const absl::optional<std::vector<uint8_t>>& early_data_context) {
-  all_select_cert_args_.push_back(
-      SelectCertArgs(server_address, client_address, hostname, client_hello,
-                     alpn, quic_transport_params, early_data_context));
+  all_select_cert_args_.push_back(SelectCertArgs(
+      server_address, client_address, ssl_capabilities, hostname, client_hello,
+      alpn, alps, quic_transport_params, early_data_context));
 
   if (select_cert_action_ == Action::DELEGATE_ASYNC ||
       select_cert_action_ == Action::FAIL_ASYNC) {
@@ -86,8 +88,9 @@
                             all_select_cert_args_.back());
     return QUIC_PENDING;
   } else if (select_cert_action_ == Action::FAIL_SYNC) {
-    callback()->OnSelectCertificateDone(/*ok=*/false,
-                                        /*is_sync=*/true, nullptr);
+    callback()->OnSelectCertificateDone(
+        /*ok=*/false,
+        /*is_sync=*/true, nullptr, /*handshake_hints=*/absl::string_view());
     return QUIC_FAILURE;
   }
 
@@ -96,7 +99,8 @@
       delegate_->GetCertChain(server_address, client_address, hostname);
 
   bool ok = chain && !chain->certs.empty();
-  callback_->OnSelectCertificateDone(ok, /*is_sync=*/true, chain.get());
+  callback_->OnSelectCertificateDone(ok, /*is_sync=*/true, chain.get(),
+                                     /*handshake_hints=*/absl::string_view());
   return ok ? QUIC_SUCCESS : QUIC_FAILURE;
 }
 
@@ -168,13 +172,15 @@
 void FakeProofSourceHandle::SelectCertOperation::Run() {
   if (action_ == Action::FAIL_ASYNC) {
     callback_->OnSelectCertificateDone(/*ok=*/false,
-                                       /*is_sync=*/false, nullptr);
+                                       /*is_sync=*/false, nullptr,
+                                       /*handshake_hints=*/absl::string_view());
   } else if (action_ == Action::DELEGATE_ASYNC) {
     QuicReferenceCountedPointer<ProofSource::Chain> chain =
         delegate_->GetCertChain(args_.server_address, args_.client_address,
                                 args_.hostname);
     bool ok = chain && !chain->certs.empty();
-    callback_->OnSelectCertificateDone(ok, /*is_sync=*/false, chain.get());
+    callback_->OnSelectCertificateDone(ok, /*is_sync=*/false, chain.get(),
+                                       /*handshake_hints=*/absl::string_view());
   } else {
     QUIC_BUG(quic_bug_10139_1)
         << "Unexpected action: " << static_cast<int>(action_);
diff --git a/quic/test_tools/fake_proof_source_handle.h b/quic/test_tools/fake_proof_source_handle.h
index 9c05ba4..d2b9918 100644
--- a/quic/test_tools/fake_proof_source_handle.h
+++ b/quic/test_tools/fake_proof_source_handle.h
@@ -40,9 +40,11 @@
   QuicAsyncStatus SelectCertificate(
       const QuicSocketAddress& server_address,
       const QuicSocketAddress& client_address,
+      absl::string_view ssl_capabilities,
       const std::string& hostname,
       absl::string_view client_hello,
       const std::string& alpn,
+      absl::optional<std::string> alps,
       const std::vector<uint8_t>& quic_transport_params,
       const absl::optional<std::vector<uint8_t>>& early_data_context) override;
 
@@ -62,24 +64,30 @@
   struct SelectCertArgs {
     SelectCertArgs(QuicSocketAddress server_address,
                    QuicSocketAddress client_address,
+                   absl::string_view ssl_capabilities,
                    std::string hostname,
                    absl::string_view client_hello,
                    std::string alpn,
+                   absl::optional<std::string> alps,
                    std::vector<uint8_t> quic_transport_params,
                    absl::optional<std::vector<uint8_t>> early_data_context)
         : server_address(server_address),
           client_address(client_address),
+          ssl_capabilities(ssl_capabilities),
           hostname(hostname),
           client_hello(client_hello),
           alpn(alpn),
+          alps(alps),
           quic_transport_params(quic_transport_params),
           early_data_context(early_data_context) {}
 
     QuicSocketAddress server_address;
     QuicSocketAddress client_address;
+    std::string ssl_capabilities;
     std::string hostname;
     std::string client_hello;
     std::string alpn;
+    absl::optional<std::string> alps;
     std::vector<uint8_t> quic_transport_params;
     absl::optional<std::vector<uint8_t>> early_data_context;
   };