No public description
PiperOrigin-RevId: 817181651
diff --git a/quiche/quic/core/crypto/transport_parameters.cc b/quiche/quic/core/crypto/transport_parameters.cc
index bdbae3b..d8235e2 100644
--- a/quiche/quic/core/crypto/transport_parameters.cc
+++ b/quiche/quic/core/crypto/transport_parameters.cc
@@ -35,6 +35,19 @@
namespace quic {
+namespace {
+std::string ObfuscateSni(const absl::string_view& input) {
+ static constexpr char kObfuscationKey[] = {0x42, 0x7b, 0x40, 0x63, 0x11, 0x2a,
+ 0x6f, 0x05, 0x58, 0x1f, 0x7f, 0x33,
+ 0x32, 0x4d, 0x64, 0x16};
+ std::string output = std::string(input);
+ for (size_t i = 0; i < output.size(); ++i) {
+ output[i] ^= kObfuscationKey[i % sizeof(kObfuscationKey)];
+ }
+ return output;
+}
+} // namespace
+
// Values of the TransportParameterId enum as defined in the
// "Transport Parameter Encoding" section of draft-ietf-quic-transport.
// When parameters are encoded, one of these enum values is used to indicate
@@ -65,6 +78,7 @@
kDiscard = 0x173E,
kGoogleHandshakeMessage = 0x26ab,
+ kDebuggingSni = 0x219bbcd0,
kInitialRoundTripTime = 0x3127,
kGoogleConnectionOptions = 0x3128,
@@ -142,6 +156,8 @@
return "discard";
case TransportParameters::kGoogleHandshakeMessage:
return "google_handshake_message";
+ case TransportParameters::kDebuggingSni:
+ return "debugging_sni";
case TransportParameters::kInitialRoundTripTime:
return "initial_round_trip_time";
case TransportParameters::kGoogleConnectionOptions:
@@ -223,6 +239,7 @@
case TransportParameters::kMaxDatagramFrameSize:
case TransportParameters::kDiscard:
case TransportParameters::kGoogleHandshakeMessage:
+ case TransportParameters::kDebuggingSni:
case TransportParameters::kInitialRoundTripTime:
case TransportParameters::kGoogleConnectionOptions:
case TransportParameters::kGoogleQuicVersion:
@@ -478,6 +495,10 @@
TransportParameterIdToString(kGoogleHandshakeMessage),
" length: ", google_handshake_message->length());
}
+ if (debugging_sni.has_value()) {
+ absl::StrAppend(&rv, " ", TransportParameterIdToString(kDebuggingSni),
+ " value: ", *debugging_sni);
+ }
rv += initial_round_trip_time_us.ToString(/*for_use_in_list=*/true);
if (google_connection_options.has_value()) {
rv += " " + TransportParameterIdToString(kGoogleConnectionOptions) + " ";
@@ -566,6 +587,7 @@
initial_round_trip_time_us(other.initial_round_trip_time_us),
discard_length(other.discard_length),
google_handshake_message(other.google_handshake_message),
+ debugging_sni(other.debugging_sni),
google_connection_options(other.google_connection_options),
custom_parameters(other.custom_parameters) {
if (other.preferred_address) {
@@ -609,6 +631,7 @@
rhs.initial_round_trip_time_us.value() &&
discard_length == rhs.discard_length &&
google_handshake_message == rhs.google_handshake_message &&
+ debugging_sni == rhs.debugging_sni &&
google_connection_options == rhs.google_connection_options &&
custom_parameters == rhs.custom_parameters)) {
return false;
@@ -684,6 +707,10 @@
*error_details = "Server cannot send google_handshake_message";
return false;
}
+ if (perspective == Perspective::IS_SERVER && debugging_sni.has_value()) {
+ *error_details = "Server cannot send debugging_sni";
+ return false;
+ }
if (perspective == Perspective::IS_SERVER &&
initial_round_trip_time_us.value() > 0) {
*error_details = "Server cannot send initial round trip time";
@@ -804,6 +831,7 @@
kIntegerParameterLength + // initial_round_trip_time_us
kTypeAndValueLength + // discard
kTypeAndValueLength + // google_handshake_message
+ kTypeAndValueLength + // debugging_sni
kTypeAndValueLength + // google_connection_options
kTypeAndValueLength; // google-version
@@ -826,6 +854,7 @@
TransportParameters::kReliableStreamReset,
TransportParameters::kDiscard,
TransportParameters::kGoogleHandshakeMessage,
+ TransportParameters::kDebuggingSni,
TransportParameters::kInitialRoundTripTime,
TransportParameters::kDisableActiveMigration,
TransportParameters::kPreferredAddress,
@@ -866,6 +895,10 @@
if (in.google_handshake_message.has_value()) {
max_transport_param_length += in.google_handshake_message->length();
}
+ // debugging_sni.
+ if (in.debugging_sni.has_value()) {
+ max_transport_param_length += in.debugging_sni->length();
+ }
// Add a random GREASE transport parameter, as defined in the
// "Reserved Transport Parameters" section of RFC 9000.
@@ -1078,6 +1111,21 @@
}
}
} break;
+ // debugging_sni.
+ case TransportParameters::kDebuggingSni: {
+ if (in.debugging_sni.has_value()) {
+ QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, in.perspective);
+ // Obfuscate the SNI before writing it to the transport parameters.
+ std::string obfuscated_sni = ObfuscateSni(*in.debugging_sni);
+ if (!writer.WriteVarInt62(TransportParameters::kDebuggingSni) ||
+ !writer.WriteStringPieceVarInt62(obfuscated_sni)) {
+ QUIC_BUG(failed_to_write_debugging_sni)
+ << "Failed to write debugging_sni: " << *in.debugging_sni
+ << " obfuscated to: " << obfuscated_sni << " for " << in;
+ return false;
+ }
+ }
+ } break;
// initial_round_trip_time_us
case TransportParameters::kInitialRoundTripTime: {
if (!in.initial_round_trip_time_us.Write(&writer)) {
@@ -1526,6 +1574,14 @@
out->google_handshake_message =
std::string(value_reader.ReadRemainingPayload());
break;
+ case TransportParameters::kDebuggingSni:
+ if (out->debugging_sni.has_value()) {
+ *error_details = "Received a second debugging_sni";
+ return false;
+ }
+ // Deobfuscate the SNI and store it in the transport parameters.
+ out->debugging_sni = ObfuscateSni(value_reader.ReadRemainingPayload());
+ break;
case TransportParameters::kInitialRoundTripTime:
parse_success =
out->initial_round_trip_time_us.Read(&value_reader, error_details);
@@ -1762,5 +1818,4 @@
parameters.version_information->other_versions = std::move(clean_versions);
}
}
-
} // namespace quic
diff --git a/quiche/quic/core/crypto/transport_parameters.h b/quiche/quic/core/crypto/transport_parameters.h
index 7467814..e82a42b 100644
--- a/quiche/quic/core/crypto/transport_parameters.h
+++ b/quiche/quic/core/crypto/transport_parameters.h
@@ -265,6 +265,10 @@
// Google internal handshake message.
std::optional<std::string> google_handshake_message;
+ // Debugging Server Name Indication. This is used to send the obfuscated SNI
+ // in the transport parameters to the server for debugging purposes only.
+ std::optional<std::string> debugging_sni;
+
// Google-specific connection options.
std::optional<QuicTagVector> google_connection_options;
diff --git a/quiche/quic/core/crypto/transport_parameters_test.cc b/quiche/quic/core/crypto/transport_parameters_test.cc
index 04f3311..bf69dc1 100644
--- a/quiche/quic/core/crypto/transport_parameters_test.cc
+++ b/quiche/quic/core/crypto/transport_parameters_test.cc
@@ -57,6 +57,8 @@
const char kFakeGoogleHandshakeMessage[] =
"01000106030392655f5230270d4964a4f99b15bbad220736d972aea97bf9ac494ead62e6";
+const char kFakeSni[] = "example.com";
+
QuicConnectionId CreateFakeOriginalDestinationConnectionId() {
return TestConnectionId(0x1337);
}
@@ -302,6 +304,7 @@
ASSERT_TRUE(absl::HexStringToBytes(kFakeGoogleHandshakeMessage,
&google_handshake_message));
orig_params.google_handshake_message = std::move(google_handshake_message);
+ orig_params.debugging_sni = kFakeSni;
orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
@@ -342,6 +345,7 @@
ASSERT_TRUE(absl::HexStringToBytes(kFakeGoogleHandshakeMessage,
&google_handshake_message));
orig_params.google_handshake_message = std::move(google_handshake_message);
+ orig_params.debugging_sni = kFakeSni;
orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
@@ -497,6 +501,14 @@
"Invalid transport parameters [Client active_connection_id_limit"
" 0 (Invalid)]");
}
+ {
+ TransportParameters params;
+ std::string error_details;
+ params.perspective = Perspective::IS_SERVER;
+ params.debugging_sni = kFakeSni;
+ EXPECT_FALSE(params.AreValid(&error_details));
+ EXPECT_EQ(error_details, "Server cannot send debugging_sni");
+ }
}
TEST_P(TransportParametersTest, NoClientParamsWithStatelessResetToken) {
@@ -587,6 +599,10 @@
0x01, 0x00, 0x01, 0x06, 0x03, 0x03, 0x92, 0x65, 0x5f, 0x52, 0x30, 0x27,
0x0d, 0x49, 0x64, 0xa4, 0xf9, 0x9b, 0x15, 0xbb, 0xad, 0x22, 0x07, 0x36,
0xd9, 0x72, 0xae, 0xa9, 0x7b, 0xf9, 0xac, 0x49, 0x4e, 0xad, 0x62, 0xe6,
+ // debugging_sni
+ 0xc0, 0x00, 0x00, 0x00, 0x21, 0x9b, 0xbc, 0xd0, // parameter id
+ 0x0b, // length
+ 0x27, 0x03, 0x21, 0x0e, 0x61, 0x46, 0x0a, 0x2b, 0x3b, 0x70, 0x12,
// initial_round_trip_time_us
0x71, 0x27, // parameter id
0x01, // length
@@ -664,8 +680,11 @@
std::string expected_google_handshake_message;
ASSERT_TRUE(absl::HexStringToBytes(kFakeGoogleHandshakeMessage,
&expected_google_handshake_message));
+
EXPECT_EQ(expected_google_handshake_message,
new_params.google_handshake_message);
+
+ EXPECT_EQ(kFakeSni, new_params.debugging_sni);
}
TEST_P(TransportParametersTest,
@@ -1106,6 +1125,8 @@
ASSERT_TRUE(absl::HexStringToBytes(kFakeGoogleHandshakeMessage,
&google_handshake_message));
orig_params.google_handshake_message = std::move(google_handshake_message);
+ orig_params.debugging_sni = kFakeSni;
+
orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
@@ -1128,6 +1149,39 @@
EXPECT_EQ(new_params, orig_params);
}
+TEST_P(TransportParametersTest, DebuggingSniParsingClientToServer) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_CLIENT;
+ orig_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationClient();
+ orig_params.debugging_sni = kFakeSni;
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized));
+
+ TransportParameters parsed_params;
+ std::string error_details;
+ ASSERT_TRUE(ParseTransportParameters(GetParam(), Perspective::IS_CLIENT,
+ serialized.data(), serialized.size(),
+ &parsed_params, &error_details));
+ EXPECT_TRUE(parsed_params.debugging_sni.has_value());
+ EXPECT_EQ(parsed_params.debugging_sni, kFakeSni);
+}
+
+TEST_P(TransportParametersTest, ServerCannotSendDebuggingSni) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_SERVER;
+ orig_params.legacy_version_information =
+ CreateFakeLegacyVersionInformationServer();
+ orig_params.debugging_sni = kFakeSni;
+
+ std::vector<uint8_t> out;
+ EXPECT_QUIC_BUG(
+ EXPECT_FALSE(SerializeTransportParameters(orig_params, &out)),
+ "Not serializing invalid transport parameters: Server cannot send "
+ "debugging_sni");
+}
+
class TransportParametersTicketSerializationTest : public QuicTest {
protected:
void SetUp() override {
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index 386112a..7d08ba8 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -6801,6 +6801,51 @@
server_thread_->Resume();
}
+TEST_P(EndToEndTest, SniInClientTransportParameters) {
+ if (!version_.UsesTls()) {
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+ const std::string debugging_sni = "test.example.com";
+ client_config_.SetDebuggingSniToSend(debugging_sni);
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable());
+
+ server_thread_->Pause();
+ QuicSpdySession* server_session = GetServerSession();
+ QuicConfig* server_config = nullptr;
+ ASSERT_TRUE(server_session);
+ server_config = server_session->config();
+ ASSERT_TRUE(server_config);
+ const std::optional<std::string>& received_sni =
+ server_config->GetReceivedDebuggingSni();
+ EXPECT_THAT(received_sni, testing::Optional(debugging_sni));
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, NoSniInClientTransportParameters) {
+ if (!version_.UsesTls()) {
+ // SNI in transport parameters is only supported with TLS.
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable());
+
+ server_thread_->Pause();
+ QuicSpdySession* server_session = GetServerSession();
+ QuicConfig* server_config = nullptr;
+ ASSERT_TRUE(server_session);
+ server_config = server_session->config();
+ ASSERT_TRUE(server_config);
+ const std::optional<std::string>& received_sni =
+ server_config->GetReceivedDebuggingSni();
+ ASSERT_FALSE(received_sni.has_value());
+ server_thread_->Resume();
+}
+
// Testing packet writer that parses initial packets and saves information
// relevant to chaos protection.
class ChaosPacketWriter : public PacketDroppingTestWriter {
diff --git a/quiche/quic/core/quic_config.cc b/quiche/quic/core/quic_config.cc
index 8e92841..222b3ad 100644
--- a/quiche/quic/core/quic_config.cc
+++ b/quiche/quic/core/quic_config.cc
@@ -507,6 +507,14 @@
return received_google_handshake_message_;
}
+void QuicConfig::SetDebuggingSniToSend(const std::string& debugging_sni) {
+ debugging_sni_to_send_ = debugging_sni;
+}
+
+const std::optional<std::string>& QuicConfig::GetReceivedDebuggingSni() const {
+ return received_debugging_sni_;
+}
+
void QuicConfig::SetDiscardLengthToSend(int32_t discard_length) {
discard_length_to_send_ = discard_length;
}
@@ -1292,6 +1300,9 @@
if (google_handshake_message_to_send_.has_value()) {
params->google_handshake_message = google_handshake_message_to_send_;
}
+ if (debugging_sni_to_send_.has_value()) {
+ params->debugging_sni = debugging_sni_to_send_;
+ }
params->discard_length = discard_length_to_send_;
@@ -1429,6 +1440,9 @@
if (params.google_handshake_message.has_value()) {
received_google_handshake_message_ = params.google_handshake_message;
}
+ if (params.debugging_sni.has_value()) {
+ received_debugging_sni_ = params.debugging_sni;
+ }
received_custom_transport_parameters_ = params.custom_parameters;
diff --git a/quiche/quic/core/quic_config.h b/quiche/quic/core/quic_config.h
index 343ca2a..05908ae 100644
--- a/quiche/quic/core/quic_config.h
+++ b/quiche/quic/core/quic_config.h
@@ -266,6 +266,10 @@
const std::optional<std::string>& GetReceivedGoogleHandshakeMessage() const;
+ void SetDebuggingSniToSend(const std::string& debugging_sni);
+
+ const std::optional<std::string>& GetReceivedDebuggingSni() const;
+
// Sets initial received connection options. All received connection options
// will be initialized with these fields. Initial received options may only be
// set once per config, prior to the setting of any other options. If options
@@ -744,6 +748,12 @@
std::optional<std::string> google_handshake_message_to_send_;
std::optional<std::string> received_google_handshake_message_;
+ // Debugging Server Name Indication. These fields are used to send and get the
+ // SNI in the transport parameters from client to the server for
+ // debugging purposes only.
+ std::optional<std::string> debugging_sni_to_send_;
+ std::optional<std::string> received_debugging_sni_;
+
// Support for RESET_STREAM_AT frame.
bool reliable_stream_reset_;
};
diff --git a/quiche/quic/core/quic_config_test.cc b/quiche/quic/core/quic_config_test.cc
index 7c4d3f9..7229ac0 100644
--- a/quiche/quic/core/quic_config_test.cc
+++ b/quiche/quic/core/quic_config_test.cc
@@ -474,6 +474,7 @@
return;
}
const std::string kFakeGoogleHandshakeMessage = "Fake handshake message";
+ const std::string kFakeSni = "example.com";
const int32_t kDiscardLength = 2000;
config_.SetInitialMaxStreamDataBytesIncomingBidirectionalToSend(
2 * kMinimumFlowControlSendWindow);
@@ -491,6 +492,7 @@
config_.SetMinAckDelayDraft10Ms(kDefaultMinAckDelayTimeMs);
config_.SetDiscardLengthToSend(kDiscardLength);
config_.SetGoogleHandshakeMessageToSend(kFakeGoogleHandshakeMessage);
+ config_.SetDebuggingSniToSend(kFakeSni);
config_.SetReliableStreamReset(true);
QuicIpAddress host;
@@ -553,6 +555,7 @@
new_stateless_reset_token);
EXPECT_EQ(kDiscardLength, params.discard_length);
EXPECT_EQ(kFakeGoogleHandshakeMessage, params.google_handshake_message);
+ EXPECT_EQ(kFakeSni, params.debugging_sni);
EXPECT_TRUE(params.reliable_stream_reset);
}
@@ -665,6 +668,7 @@
return;
}
const std::string kFakeGoogleHandshakeMessage = "Fake handshake message";
+ const std::string kFakeSni = "example.com";
const int32_t kDiscardLength = 2000;
TransportParameters params;
@@ -687,6 +691,7 @@
params.retry_source_connection_id = TestConnectionId(0x3333);
params.discard_length = kDiscardLength;
params.google_handshake_message = kFakeGoogleHandshakeMessage;
+ params.debugging_sni = kFakeSni;
std::string error_details;
EXPECT_THAT(config_.ProcessTransportParameters(
@@ -807,6 +812,7 @@
TestConnectionId(0x3333));
EXPECT_EQ(kFakeGoogleHandshakeMessage,
config_.GetReceivedGoogleHandshakeMessage());
+ EXPECT_EQ(kFakeSni, config_.GetReceivedDebuggingSni());
EXPECT_EQ(kDiscardLength, config_.GetDiscardLengthReceived());
}