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;
};