Add ALPN to QUIC when using TLS
This CL makes our client send ALPN when using QUIC with TLS, makes the server echo the first ALPN value, and allows quic_client to override the ALPN for IETF interop events.
gfe-relnote: protected by disabled flag quic_supports_tls_handshake
PiperOrigin-RevId: 242682444
Change-Id: I7e60fb61c0afe02283e38598de29df9018b71ee8
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc
index 62f9ce9..ca6bdae 100644
--- a/quic/core/quic_versions.cc
+++ b/quic/core/quic_versions.cc
@@ -25,9 +25,9 @@
return MakeQuicTag(d, c, b, a);
}
-// Version label for ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99).
-// Defaults to "T099". Can be overridden for IETF interop events.
-QuicVersionLabel kQuicT099VersionLabel = 0;
+// IETF draft version for ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99).
+// Overrides the version label and ALPN string for IETF interop events.
+int32_t kQuicT099IetfDraftVersion = 0;
} // namespace
@@ -73,8 +73,8 @@
return MakeVersionLabel(proto, '0', '4', '7');
case QUIC_VERSION_99:
if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 &&
- kQuicT099VersionLabel != 0) {
- return kQuicT099VersionLabel;
+ kQuicT099IetfDraftVersion != 0) {
+ return 0xff000000 + kQuicT099IetfDraftVersion;
}
return MakeVersionLabel(proto, '0', '9', '9');
default:
@@ -116,7 +116,7 @@
}
ParsedQuicVersion ParseQuicVersionString(std::string version_string) {
- if (version_string.length() == 0) {
+ if (version_string.empty()) {
return UnsupportedQuicVersion();
}
int quic_version_number = 0;
@@ -140,7 +140,7 @@
}
}
}
- // Still recognize T099 even if kQuicT099VersionLabel has been changed.
+ // Still recognize T099 even if kQuicT099IetfDraftVersion has been changed.
if (FLAGS_quic_supports_tls_handshake && version_string == "T099") {
return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99);
}
@@ -378,6 +378,15 @@
return ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED);
}
+std::string AlpnForVersion(ParsedQuicVersion parsed_version) {
+ if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 &&
+ parsed_version.transport_version == QUIC_VERSION_99 &&
+ kQuicT099IetfDraftVersion != 0) {
+ return "h3-" + QuicTextUtils::Uint64ToString(kQuicT099IetfDraftVersion);
+ }
+ return "h3-google-" + ParsedQuicVersionToString(parsed_version);
+}
+
void QuicVersionInitializeSupportForIetfDraft(int32_t draft_version) {
if (draft_version == 0) {
return;
@@ -387,7 +396,7 @@
return;
}
- kQuicT099VersionLabel = 0xff000000 + draft_version;
+ kQuicT099IetfDraftVersion = draft_version;
// Enable necessary flags.
SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h
index c270f95..cc780b4 100644
--- a/quic/core/quic_versions.h
+++ b/quic/core/quic_versions.h
@@ -359,6 +359,10 @@
return transport_version == QUIC_VERSION_99;
}
+// Returns the ALPN string to use in TLS for this version of QUIC.
+QUIC_EXPORT_PRIVATE std::string AlpnForVersion(
+ ParsedQuicVersion parsed_version);
+
// Initializes support for the provided IETF draft version by setting flags
// and the version label.
QUIC_EXPORT_PRIVATE void QuicVersionInitializeSupportForIetfDraft(
diff --git a/quic/core/quic_versions_test.cc b/quic/core/quic_versions_test.cc
index 6139ca8..09ed5e8 100644
--- a/quic/core/quic_versions_test.cc
+++ b/quic/core/quic_versions_test.cc
@@ -556,6 +556,21 @@
EXPECT_EQ(QUIC_VERSION_99, 99);
}
+TEST_F(QuicVersionsTest, AlpnForVersion) {
+ FLAGS_quic_supports_tls_handshake = true;
+ ParsedQuicVersion parsed_version_q047 =
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47);
+ ParsedQuicVersion parsed_version_t047 =
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47);
+ ParsedQuicVersion parsed_version_t099 =
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99);
+ FLAGS_quic_supports_tls_handshake = false;
+
+ EXPECT_EQ("h3-google-Q047", AlpnForVersion(parsed_version_q047));
+ EXPECT_EQ("h3-google-T047", AlpnForVersion(parsed_version_t047));
+ EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099));
+}
+
TEST_F(QuicVersionsTest, InitializeSupportForIetfDraft) {
FLAGS_quic_supports_tls_handshake = true;
ParsedQuicVersion parsed_version_t099 =
@@ -563,16 +578,19 @@
FLAGS_quic_supports_tls_handshake = false;
EXPECT_EQ(MakeVersionLabel('T', '0', '9', '9'),
CreateQuicVersionLabel(parsed_version_t099));
+ EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099));
QuicVersionInitializeSupportForIetfDraft(0);
EXPECT_EQ(MakeVersionLabel('T', '0', '9', '9'),
CreateQuicVersionLabel(parsed_version_t099));
+ EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099));
EXPECT_FALSE(FLAGS_quic_supports_tls_handshake);
QuicVersionInitializeSupportForIetfDraft(18);
EXPECT_TRUE(FLAGS_quic_supports_tls_handshake);
EXPECT_EQ(MakeVersionLabel(0xff, 0, 0, 18),
CreateQuicVersionLabel(parsed_version_t099));
+ EXPECT_EQ("h3-18", AlpnForVersion(parsed_version_t099));
}
TEST_F(QuicVersionsTest, QuicEnableVersion) {
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index adc2c81..5081a48 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -4,12 +4,14 @@
#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include <cstring>
#include <string>
#include "third_party/boringssl/src/include/openssl/ssl.h"
#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
namespace quic {
@@ -87,6 +89,27 @@
return false;
}
+ std::string alpn_string =
+ AlpnForVersion(session()->supported_versions().front());
+ if (alpn_string.length() > std::numeric_limits<uint8_t>::max()) {
+ QUIC_BUG << "ALPN too long: '" << alpn_string << "'";
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "ALPN too long");
+ return false;
+ }
+ const uint8_t alpn_length = alpn_string.length();
+ // SSL_set_alpn_protos expects a sequence of one-byte-length-prefixed strings
+ // so we copy alpn_string to a new buffer that has the length in alpn[0].
+ uint8_t alpn[std::numeric_limits<uint8_t>::max() + 1];
+ alpn[0] = alpn_length;
+ memcpy(reinterpret_cast<char*>(alpn + 1), alpn_string.data(), alpn_length);
+ if (SSL_set_alpn_protos(ssl(), alpn,
+ static_cast<unsigned>(alpn_length) + 1) != 0) {
+ QUIC_BUG << "Failed to set ALPN: '" << alpn_string << "'";
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "Failed to set ALPN");
+ return false;
+ }
+ QUIC_DLOG(INFO) << "Client using ALPN: '" << alpn_string << "'";
+
// Set the Transport Parameters to send in the ClientHello
if (!SetTransportParameters()) {
CloseConnection(QUIC_HANDSHAKE_FAILED,
@@ -248,6 +271,28 @@
return;
}
+ const uint8_t* alpn_data = nullptr;
+ unsigned alpn_length = 0;
+ SSL_get0_alpn_selected(ssl(), &alpn_data, &alpn_length);
+ // TODO(b/130164908) Act on ALPN.
+ if (alpn_length != 0) {
+ std::string received_alpn_string(reinterpret_cast<const char*>(alpn_data),
+ alpn_length);
+ std::string sent_alpn_string =
+ AlpnForVersion(session()->supported_versions().front());
+ if (received_alpn_string != sent_alpn_string) {
+ QUIC_LOG(ERROR) << "Client: received mismatched ALPN '"
+ << received_alpn_string << "', expected '"
+ << sent_alpn_string << "'";
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "Mismatched ALPN");
+ return;
+ }
+ QUIC_DLOG(INFO) << "Client: server selected ALPN: '" << received_alpn_string
+ << "'";
+ } else {
+ QUIC_DLOG(INFO) << "Client: server did not select ALPN";
+ }
+
session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session()->NeuterUnencryptedData();
encryption_established_ = true;
diff --git a/quic/core/tls_handshaker.cc b/quic/core/tls_handshaker.cc
index 17e2674..c6394b8 100644
--- a/quic/core/tls_handshaker.cc
+++ b/quic/core/tls_handshaker.cc
@@ -236,6 +236,8 @@
// (draft-ietf-quic-transport-14, section 11.3). However, according to
// quic_error_codes.h, this QUIC implementation only sends 1-byte error codes
// right now.
+ QUIC_DLOG(INFO) << "TLS failing handshake due to alert "
+ << static_cast<int>(desc);
CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failure");
}
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index 16c84b1..96e1802 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -51,6 +51,8 @@
bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsHandshaker::CreateSslCtx();
SSL_CTX_set_tlsext_servername_callback(
ssl_ctx.get(), TlsServerHandshaker::SelectCertificateCallback);
+ SSL_CTX_set_alpn_select_cb(ssl_ctx.get(),
+ TlsServerHandshaker::SelectAlpnCallback, nullptr);
return ssl_ctx;
}
@@ -363,4 +365,41 @@
return SSL_TLSEXT_ERR_OK;
}
+// static
+int TlsServerHandshaker::SelectAlpnCallback(SSL* ssl,
+ const uint8_t** out,
+ uint8_t* out_len,
+ const uint8_t* in,
+ unsigned in_len,
+ void* arg) {
+ return HandshakerFromSsl(ssl)->SelectAlpn(out, out_len, in, in_len);
+}
+
+int TlsServerHandshaker::SelectAlpn(const uint8_t** out,
+ uint8_t* out_len,
+ const uint8_t* in,
+ unsigned in_len) {
+ // |in| contains a sequence of 1-byte-length-prefixed values.
+ // We currently simply return the first provided ALPN value.
+ // TODO(b/130164908) Act on ALPN.
+ if (in_len == 0) {
+ *out_len = 0;
+ *out = nullptr;
+ QUIC_DLOG(INFO) << "No ALPN provided";
+ return SSL_TLSEXT_ERR_OK;
+ }
+ const uint8_t first_alpn_length = in[0];
+ if (static_cast<unsigned>(first_alpn_length) > in_len - 1) {
+ QUIC_LOG(ERROR) << "Failed to parse ALPN";
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ *out_len = first_alpn_length;
+ *out = in + 1;
+ QUIC_DLOG(INFO) << "Server selecting ALPN '"
+ << QuicStringPiece(reinterpret_cast<const char*>(*out),
+ *out_len)
+ << "'";
+ return SSL_TLSEXT_ERR_OK;
+}
+
} // namespace quic
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h
index 1e9b497..e27d896 100644
--- a/quic/core/tls_server_handshaker.h
+++ b/quic/core/tls_server_handshaker.h
@@ -61,6 +61,14 @@
// |ssl|.
static int SelectCertificateCallback(SSL* ssl, int* out_alert, void* arg);
+ // Calls SelectAlpn after looking up the TlsServerHandshaker from |ssl|.
+ static int SelectAlpnCallback(SSL* ssl,
+ const uint8_t** out,
+ uint8_t* out_len,
+ const uint8_t* in,
+ unsigned in_len,
+ void* arg);
+
private:
class SignatureCallback : public ProofSource::SignatureCallback {
public:
@@ -148,6 +156,12 @@
// |*out_alert| the TLS alert value that the server will send.
int SelectCertificate(int* out_alert);
+ // Selects which ALPN to use based on the list sent by the client.
+ int SelectAlpn(const uint8_t** out,
+ uint8_t* out_len,
+ const uint8_t* in,
+ unsigned in_len);
+
static TlsServerHandshaker* HandshakerFromSsl(SSL* ssl);
State state_ = STATE_LISTENING;