In TlsServerHandshaker, switch from BoringSSL's tlsext_servername_callback to select_cert_cb for cert selection.
This paves the way to go/split-handshake-v2.
Protected by FLAGS_quic_reloadable_flag_quic_tls_use_early_select_cert.
PiperOrigin-RevId: 343531889
Change-Id: Ib0c677c18f50526fc86ed45b532f460cdedcd119
diff --git a/quic/core/crypto/tls_server_connection.cc b/quic/core/crypto/tls_server_connection.cc
index f47e5ad..0208a9c 100644
--- a/quic/core/crypto/tls_server_connection.cc
+++ b/quic/core/crypto/tls_server_connection.cc
@@ -40,6 +40,8 @@
GetQuicRestartFlag(quic_session_tickets_always_enabled))) {
SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1);
}
+ SSL_CTX_set_select_certificate_cb(
+ ssl_ctx.get(), &TlsServerConnection::EarlySelectCertCallback);
return ssl_ctx;
}
@@ -62,6 +64,13 @@
}
// static
+ssl_select_cert_result_t TlsServerConnection::EarlySelectCertCallback(
+ const SSL_CLIENT_HELLO* client_hello) {
+ return ConnectionFromSsl(client_hello->ssl)
+ ->delegate_->EarlySelectCertCallback(client_hello);
+}
+
+// static
int TlsServerConnection::SelectCertificateCallback(SSL* ssl,
int* out_alert,
void* /*arg*/) {
diff --git a/quic/core/crypto/tls_server_connection.h b/quic/core/crypto/tls_server_connection.h
index 954f830..50c6f7c 100644
--- a/quic/core/crypto/tls_server_connection.h
+++ b/quic/core/crypto/tls_server_connection.h
@@ -22,12 +22,19 @@
virtual ~Delegate() {}
protected:
+ // Called from BoringSSL right after SNI is extracted, which is very early
+ // in the handshake process.
+ virtual ssl_select_cert_result_t EarlySelectCertCallback(
+ const SSL_CLIENT_HELLO* client_hello) = 0;
+
// Configures the certificate to use on |ssl_| based on the SNI sent by the
// client. Returns an SSL_TLSEXT_ERR_* value (see
// https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_tlsext_servername_callback).
//
// If SelectCertificate returns SSL_TLSEXT_ERR_ALERT_FATAL, then it puts in
// |*out_alert| the TLS alert value that the server will send.
+ //
+ // TODO(wub): Deprecate it after enabling --quic_tls_use_early_select_cert.
virtual int SelectCertificate(int* out_alert) = 0;
// Selects which ALPN to use based on the list sent by the client.
@@ -121,6 +128,9 @@
// Specialization of TlsConnection::ConnectionFromSsl.
static TlsServerConnection* ConnectionFromSsl(SSL* ssl);
+ static ssl_select_cert_result_t EarlySelectCertCallback(
+ const SSL_CLIENT_HELLO* client_hello);
+
// These functions are registered as callbacks in BoringSSL and delegate their
// implementation to the matching methods in Delegate above.
static int SelectCertificateCallback(SSL* ssl, int* out_alert, void* arg);
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 10c0cb7..2fd5426 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -79,6 +79,7 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_stop_sending_uses_ietf_error_code, 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_use_early_select_cert, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_circular_deque_for_unacked_packets, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_encryption_level_context, false)
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index 01b5df6..38a4638 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -243,12 +243,26 @@
}
bool TlsServerHandshaker::ProcessTransportParameters(
+ const SSL_CLIENT_HELLO* client_hello,
std::string* error_details) {
TransportParameters client_params;
const uint8_t* client_params_bytes;
size_t params_bytes_len;
- SSL_get_peer_quic_transport_params(ssl(), &client_params_bytes,
- ¶ms_bytes_len);
+ if (use_early_select_cert_) {
+ // When using early select cert callback, SSL_get_peer_quic_transport_params
+ // can not be used to retrieve the client's transport parameters, but we can
+ // use SSL_early_callback_ctx_extension_get to do that.
+ if (!SSL_early_callback_ctx_extension_get(
+ client_hello, TLSEXT_TYPE_quic_transport_parameters,
+ &client_params_bytes, ¶ms_bytes_len)) {
+ params_bytes_len = 0;
+ }
+ } else {
+ DCHECK_EQ(client_hello, nullptr);
+ SSL_get_peer_quic_transport_params(ssl(), &client_params_bytes,
+ ¶ms_bytes_len);
+ }
+
if (params_bytes_len == 0) {
*error_details = "Client's transport parameters are missing";
return false;
@@ -506,6 +520,62 @@
return ssl_ticket_aead_success;
}
+ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback(
+ const SSL_CLIENT_HELLO* client_hello) {
+ if (!use_early_select_cert_) {
+ return ssl_select_cert_success;
+ }
+
+ if (!pre_shared_key_.empty()) {
+ // TODO(b/154162689) add PSK support to QUIC+TLS.
+ QUIC_BUG << "QUIC server pre-shared keys not yet supported with TLS";
+ return ssl_select_cert_error;
+ }
+
+ // This callback is called very early by Boring SSL, most of the SSL_get_foo
+ // function do not work at this point, but SSL_get_servername does.
+ const char* hostname = SSL_get_servername(ssl(), TLSEXT_NAMETYPE_host_name);
+ if (hostname) {
+ hostname_ = hostname;
+ crypto_negotiated_params_->sni =
+ QuicHostnameUtils::NormalizeHostname(hostname_);
+ if (!ValidateHostname(hostname_)) {
+ return ssl_select_cert_error;
+ }
+ } else {
+ QUIC_LOG(INFO) << "No hostname indicated in SNI";
+ }
+
+ QuicReferenceCountedPointer<ProofSource::Chain> chain =
+ proof_source_->GetCertChain(session()->connection()->self_address(),
+ session()->connection()->peer_address(),
+ hostname_);
+ if (!chain || chain->certs.empty()) {
+ QUIC_LOG(ERROR) << "No certs provided for host '" << hostname_ << "'";
+ return ssl_select_cert_error;
+ }
+
+ CryptoBuffers cert_buffers = chain->ToCryptoBuffers();
+ tls_connection_.SetCertChain(cert_buffers.value);
+
+ std::string error_details;
+ if (!ProcessTransportParameters(client_hello, &error_details)) {
+ CloseConnection(QUIC_HANDSHAKE_FAILED, error_details);
+ return ssl_select_cert_error;
+ }
+ OverrideQuicConfigDefaults(session()->config());
+ session()->OnConfigNegotiated();
+
+ if (!SetTransportParameters()) {
+ QUIC_LOG(ERROR) << "Failed to set transport parameters";
+ return ssl_select_cert_error;
+ }
+
+ QUIC_DLOG(INFO) << "Set " << chain->certs.size() << " certs for server "
+ << "with hostname " << hostname_;
+ return ssl_select_cert_success;
+}
+
bool TlsServerHandshaker::ValidateHostname(const std::string& hostname) const {
if (!QuicHostnameUtils::IsValidSNI(hostname)) {
// TODO(b/151676147): Include this error string in the CONNECTION_CLOSE
@@ -517,6 +587,10 @@
}
int TlsServerHandshaker::SelectCertificate(int* out_alert) {
+ if (use_early_select_cert_) {
+ return SSL_TLSEXT_ERR_OK;
+ }
+
const char* hostname = SSL_get_servername(ssl(), TLSEXT_NAMETYPE_host_name);
if (hostname) {
hostname_ = hostname;
@@ -548,7 +622,7 @@
tls_connection_.SetCertChain(cert_buffers.value);
std::string error_details;
- if (!ProcessTransportParameters(&error_details)) {
+ if (!ProcessTransportParameters(nullptr, &error_details)) {
CloseConnection(QUIC_HANDSHAKE_FAILED, error_details);
*out_alert = SSL_AD_INTERNAL_ERROR;
return SSL_TLSEXT_ERR_ALERT_FATAL;
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h
index c6426ce..09d03eb 100644
--- a/quic/core/tls_server_handshaker.h
+++ b/quic/core/tls_server_handshaker.h
@@ -103,6 +103,9 @@
const ProofVerifyDetails& verify_details) override;
// TlsServerConnection::Delegate implementation:
+ // Used to select certificates and process transport parameters.
+ ssl_select_cert_result_t EarlySelectCertCallback(
+ const SSL_CLIENT_HELLO* client_hello) override;
int SelectCertificate(int* out_alert) override;
int SelectAlpn(const uint8_t** out,
uint8_t* out_len,
@@ -158,7 +161,8 @@
virtual bool ValidateHostname(const std::string& hostname) const;
bool SetTransportParameters();
- bool ProcessTransportParameters(std::string* error_details);
+ bool ProcessTransportParameters(const SSL_CLIENT_HELLO* client_hello,
+ std::string* error_details);
ProofSource* proof_source_;
SignatureCallback* signature_callback_ = nullptr;
@@ -192,6 +196,8 @@
QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters>
crypto_negotiated_params_;
TlsServerConnection tls_connection_;
+ const bool use_early_select_cert_ =
+ GetQuicReloadableFlag(quic_tls_use_early_select_cert);
};
} // namespace quic