1 // Copyright (c) 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/core/tls_chlo_extractor.h"
6
7 #include <memory>
8
9 #include "openssl/ssl.h"
10 #include "quiche/quic/core/http/quic_spdy_client_session.h"
11 #include "quiche/quic/core/quic_connection.h"
12 #include "quiche/quic/core/quic_types.h"
13 #include "quiche/quic/core/quic_versions.h"
14 #include "quiche/quic/platform/api/quic_test.h"
15 #include "quiche/quic/test_tools/crypto_test_utils.h"
16 #include "quiche/quic/test_tools/first_flight.h"
17 #include "quiche/quic/test_tools/quic_test_utils.h"
18 #include "quiche/quic/test_tools/simple_session_cache.h"
19
20 namespace quic {
21 namespace test {
22 namespace {
23
24 using testing::_;
25 using testing::AnyNumber;
26
27 class TlsChloExtractorTest : public QuicTestWithParam<ParsedQuicVersion> {
28 protected:
TlsChloExtractorTest()29 TlsChloExtractorTest() : version_(GetParam()), server_id_(TestServerId()) {}
30
Initialize()31 void Initialize() {
32 tls_chlo_extractor_ = std::make_unique<TlsChloExtractor>();
33 AnnotatedPackets packets =
34 GetAnnotatedFirstFlightOfPackets(version_, config_);
35 packets_ = std::move(packets.packets);
36 crypto_stream_size_ = packets.crypto_stream_size;
37 QUIC_DLOG(INFO) << "Initialized with " << packets_.size()
38 << " packets with crypto_stream_size:"
39 << crypto_stream_size_;
40 }
41
Initialize(std::unique_ptr<QuicCryptoClientConfig> crypto_config)42 void Initialize(std::unique_ptr<QuicCryptoClientConfig> crypto_config) {
43 tls_chlo_extractor_ = std::make_unique<TlsChloExtractor>();
44 AnnotatedPackets packets = GetAnnotatedFirstFlightOfPackets(
45 version_, config_, TestConnectionId(), EmptyQuicConnectionId(),
46 std::move(crypto_config));
47 packets_ = std::move(packets.packets);
48 crypto_stream_size_ = packets.crypto_stream_size;
49 QUIC_DLOG(INFO) << "Initialized with " << packets_.size()
50 << " packets with crypto_stream_size:"
51 << crypto_stream_size_;
52 }
53
54 // Perform a full handshake in order to insert a SSL_SESSION into
55 // crypto_config->session_cache(), which can be used by a TLS resumption.
PerformFullHandshake(QuicCryptoClientConfig * crypto_config) const56 void PerformFullHandshake(QuicCryptoClientConfig* crypto_config) const {
57 ASSERT_NE(crypto_config->session_cache(), nullptr);
58 MockQuicConnectionHelper client_helper, server_helper;
59 MockAlarmFactory alarm_factory;
60 ParsedQuicVersionVector supported_versions = {version_};
61 PacketSavingConnection* client_connection =
62 new PacketSavingConnection(&client_helper, &alarm_factory,
63 Perspective::IS_CLIENT, supported_versions);
64 // Advance the time, because timers do not like uninitialized times.
65 client_connection->AdvanceTime(QuicTime::Delta::FromSeconds(1));
66 QuicSpdyClientSession client_session(config_, supported_versions,
67 client_connection, server_id_,
68 crypto_config);
69 client_session.Initialize();
70
71 std::unique_ptr<QuicCryptoServerConfig> server_crypto_config =
72 crypto_test_utils::CryptoServerConfigForTesting();
73 QuicConfig server_config;
74
75 EXPECT_CALL(*client_connection, SendCryptoData(_, _, _)).Times(AnyNumber());
76 client_session.GetMutableCryptoStream()->CryptoConnect();
77
78 crypto_test_utils::HandshakeWithFakeServer(
79 &server_config, server_crypto_config.get(), &server_helper,
80 &alarm_factory, client_connection,
81 client_session.GetMutableCryptoStream(),
82 AlpnForVersion(client_connection->version()));
83
84 // For some reason, the test client can not receive the server settings and
85 // the SSL_SESSION will not be inserted to client's session_cache. We create
86 // a dummy settings and call SetServerApplicationStateForResumption manually
87 // to ensure the SSL_SESSION is cached.
88 // TODO(wub): Fix crypto_test_utils::HandshakeWithFakeServer to make sure a
89 // SSL_SESSION is cached at the client, and remove the rest of the function.
90 SettingsFrame server_settings;
91 server_settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] =
92 kDefaultQpackMaxDynamicTableCapacity;
93 std::string settings_frame =
94 HttpEncoder::SerializeSettingsFrame(server_settings);
95 client_session.GetMutableCryptoStream()
96 ->SetServerApplicationStateForResumption(
97 std::make_unique<ApplicationState>(
98 settings_frame.data(),
99 settings_frame.data() + settings_frame.length()));
100 }
101
IngestPackets()102 void IngestPackets() {
103 for (const std::unique_ptr<QuicReceivedPacket>& packet : packets_) {
104 ReceivedPacketInfo packet_info(
105 QuicSocketAddress(TestPeerIPAddress(), kTestPort),
106 QuicSocketAddress(TestPeerIPAddress(), kTestPort), *packet);
107 std::string detailed_error;
108 std::optional<absl::string_view> retry_token;
109 const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher(
110 *packet, /*expected_destination_connection_id_length=*/0,
111 &packet_info.form, &packet_info.long_packet_type,
112 &packet_info.version_flag, &packet_info.use_length_prefix,
113 &packet_info.version_label, &packet_info.version,
114 &packet_info.destination_connection_id,
115 &packet_info.source_connection_id, &retry_token, &detailed_error);
116 ASSERT_THAT(error, IsQuicNoError()) << detailed_error;
117 tls_chlo_extractor_->IngestPacket(packet_info.version,
118 packet_info.packet);
119 }
120 packets_.clear();
121 }
122
ValidateChloDetails(const TlsChloExtractor * extractor=nullptr) const123 void ValidateChloDetails(const TlsChloExtractor* extractor = nullptr) const {
124 if (extractor == nullptr) {
125 extractor = tls_chlo_extractor_.get();
126 }
127
128 EXPECT_TRUE(extractor->HasParsedFullChlo());
129 std::vector<std::string> alpns = extractor->alpns();
130 ASSERT_EQ(alpns.size(), 1u);
131 EXPECT_EQ(alpns[0], AlpnForVersion(version_));
132 EXPECT_EQ(extractor->server_name(), TestHostname());
133 // Crypto stream has one frame in the following format:
134 // CRYPTO Frame {
135 // Type (i) = 0x06,
136 // Offset (i),
137 // Length (i),
138 // Crypto Data (..),
139 // }
140 //
141 // Type is 1 byte long, Offset is zero and also 1 byte long, and
142 // all generated ClientHello messages have 2 byte length. So
143 // the header is 4 bytes total.
144 EXPECT_EQ(extractor->client_hello_bytes().size(), crypto_stream_size_ - 4);
145 }
146
IncreaseSizeOfChlo()147 void IncreaseSizeOfChlo() {
148 // Add a 2000-byte custom parameter to increase the length of the CHLO.
149 constexpr auto kCustomParameterId =
150 static_cast<TransportParameters::TransportParameterId>(0xff33);
151 std::string kCustomParameterValue(2000, '-');
152 config_.custom_transport_parameters_to_send()[kCustomParameterId] =
153 kCustomParameterValue;
154 }
155
156 ParsedQuicVersion version_;
157 QuicServerId server_id_;
158 std::unique_ptr<TlsChloExtractor> tls_chlo_extractor_;
159 QuicConfig config_;
160 std::vector<std::unique_ptr<QuicReceivedPacket>> packets_;
161 uint64_t crypto_stream_size_;
162 };
163
164 INSTANTIATE_TEST_SUITE_P(TlsChloExtractorTests, TlsChloExtractorTest,
165 ::testing::ValuesIn(AllSupportedVersionsWithTls()),
166 ::testing::PrintToStringParamName());
167
TEST_P(TlsChloExtractorTest,Simple)168 TEST_P(TlsChloExtractorTest, Simple) {
169 Initialize();
170 EXPECT_EQ(packets_.size(), 1u);
171 IngestPackets();
172 ValidateChloDetails();
173 EXPECT_EQ(tls_chlo_extractor_->state(),
174 TlsChloExtractor::State::kParsedFullSinglePacketChlo);
175 EXPECT_FALSE(tls_chlo_extractor_->resumption_attempted());
176 EXPECT_FALSE(tls_chlo_extractor_->early_data_attempted());
177 }
178
TEST_P(TlsChloExtractorTest,TlsExtensionInfo_ResumptionOnly)179 TEST_P(TlsChloExtractorTest, TlsExtensionInfo_ResumptionOnly) {
180 auto crypto_client_config = std::make_unique<QuicCryptoClientConfig>(
181 crypto_test_utils::ProofVerifierForTesting(),
182 std::make_unique<SimpleSessionCache>());
183 PerformFullHandshake(crypto_client_config.get());
184
185 SSL_CTX_set_early_data_enabled(crypto_client_config->ssl_ctx(), 0);
186 Initialize(std::move(crypto_client_config));
187 EXPECT_GE(packets_.size(), 1u);
188 IngestPackets();
189 ValidateChloDetails();
190 EXPECT_EQ(tls_chlo_extractor_->state(),
191 TlsChloExtractor::State::kParsedFullSinglePacketChlo);
192 EXPECT_TRUE(tls_chlo_extractor_->resumption_attempted());
193 EXPECT_FALSE(tls_chlo_extractor_->early_data_attempted());
194 }
195
TEST_P(TlsChloExtractorTest,TlsExtensionInfo_ZeroRtt)196 TEST_P(TlsChloExtractorTest, TlsExtensionInfo_ZeroRtt) {
197 auto crypto_client_config = std::make_unique<QuicCryptoClientConfig>(
198 crypto_test_utils::ProofVerifierForTesting(),
199 std::make_unique<SimpleSessionCache>());
200 PerformFullHandshake(crypto_client_config.get());
201
202 IncreaseSizeOfChlo();
203 Initialize(std::move(crypto_client_config));
204 EXPECT_GE(packets_.size(), 1u);
205 IngestPackets();
206 ValidateChloDetails();
207 EXPECT_EQ(tls_chlo_extractor_->state(),
208 TlsChloExtractor::State::kParsedFullMultiPacketChlo);
209 EXPECT_TRUE(tls_chlo_extractor_->resumption_attempted());
210 EXPECT_TRUE(tls_chlo_extractor_->early_data_attempted());
211 }
212
TEST_P(TlsChloExtractorTest,TlsExtensionInfo_SupportedGroups)213 TEST_P(TlsChloExtractorTest, TlsExtensionInfo_SupportedGroups) {
214 const std::vector<std::vector<uint16_t>> preferred_groups_to_test = {
215 // Only one group
216 {SSL_GROUP_X25519},
217 // Two groups
218 {SSL_GROUP_X25519_KYBER768_DRAFT00, SSL_GROUP_X25519},
219 };
220 for (const std::vector<uint16_t>& preferred_groups :
221 preferred_groups_to_test) {
222 auto crypto_client_config = std::make_unique<QuicCryptoClientConfig>(
223 crypto_test_utils::ProofVerifierForTesting());
224 crypto_client_config->set_preferred_groups(preferred_groups);
225
226 Initialize(std::move(crypto_client_config));
227 IngestPackets();
228 ValidateChloDetails();
229 EXPECT_EQ(tls_chlo_extractor_->supported_groups(), preferred_groups);
230 }
231 }
232
TEST_P(TlsChloExtractorTest,MultiPacket)233 TEST_P(TlsChloExtractorTest, MultiPacket) {
234 IncreaseSizeOfChlo();
235 Initialize();
236 EXPECT_EQ(packets_.size(), 2u);
237 IngestPackets();
238 ValidateChloDetails();
239 EXPECT_EQ(tls_chlo_extractor_->state(),
240 TlsChloExtractor::State::kParsedFullMultiPacketChlo);
241 }
242
TEST_P(TlsChloExtractorTest,MultiPacketReordered)243 TEST_P(TlsChloExtractorTest, MultiPacketReordered) {
244 IncreaseSizeOfChlo();
245 Initialize();
246 ASSERT_EQ(packets_.size(), 2u);
247 // Artificially reorder both packets.
248 std::swap(packets_[0], packets_[1]);
249 IngestPackets();
250 ValidateChloDetails();
251 EXPECT_EQ(tls_chlo_extractor_->state(),
252 TlsChloExtractor::State::kParsedFullMultiPacketChlo);
253 }
254
TEST_P(TlsChloExtractorTest,MoveAssignment)255 TEST_P(TlsChloExtractorTest, MoveAssignment) {
256 Initialize();
257 EXPECT_EQ(packets_.size(), 1u);
258 TlsChloExtractor other_extractor;
259 *tls_chlo_extractor_ = std::move(other_extractor);
260 IngestPackets();
261 ValidateChloDetails();
262 EXPECT_EQ(tls_chlo_extractor_->state(),
263 TlsChloExtractor::State::kParsedFullSinglePacketChlo);
264 }
265
TEST_P(TlsChloExtractorTest,MoveAssignmentAfterExtraction)266 TEST_P(TlsChloExtractorTest, MoveAssignmentAfterExtraction) {
267 Initialize();
268 EXPECT_EQ(packets_.size(), 1u);
269 IngestPackets();
270 ValidateChloDetails();
271 EXPECT_EQ(tls_chlo_extractor_->state(),
272 TlsChloExtractor::State::kParsedFullSinglePacketChlo);
273
274 TlsChloExtractor other_extractor = std::move(*tls_chlo_extractor_);
275
276 EXPECT_EQ(other_extractor.state(),
277 TlsChloExtractor::State::kParsedFullSinglePacketChlo);
278 ValidateChloDetails(&other_extractor);
279 }
280
TEST_P(TlsChloExtractorTest,MoveAssignmentBetweenPackets)281 TEST_P(TlsChloExtractorTest, MoveAssignmentBetweenPackets) {
282 IncreaseSizeOfChlo();
283 Initialize();
284 ASSERT_EQ(packets_.size(), 2u);
285 TlsChloExtractor other_extractor;
286
287 // Have |other_extractor| parse the first packet.
288 ReceivedPacketInfo packet_info(
289 QuicSocketAddress(TestPeerIPAddress(), kTestPort),
290 QuicSocketAddress(TestPeerIPAddress(), kTestPort), *packets_[0]);
291 std::string detailed_error;
292 std::optional<absl::string_view> retry_token;
293 const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher(
294 *packets_[0], /*expected_destination_connection_id_length=*/0,
295 &packet_info.form, &packet_info.long_packet_type,
296 &packet_info.version_flag, &packet_info.use_length_prefix,
297 &packet_info.version_label, &packet_info.version,
298 &packet_info.destination_connection_id, &packet_info.source_connection_id,
299 &retry_token, &detailed_error);
300 ASSERT_THAT(error, IsQuicNoError()) << detailed_error;
301 other_extractor.IngestPacket(packet_info.version, packet_info.packet);
302 // Remove the first packet from the list.
303 packets_.erase(packets_.begin());
304 EXPECT_EQ(packets_.size(), 1u);
305
306 // Move the extractor.
307 *tls_chlo_extractor_ = std::move(other_extractor);
308
309 // Have |tls_chlo_extractor_| parse the second packet.
310 IngestPackets();
311
312 ValidateChloDetails();
313 EXPECT_EQ(tls_chlo_extractor_->state(),
314 TlsChloExtractor::State::kParsedFullMultiPacketChlo);
315 }
316
317 } // namespace
318 } // namespace test
319 } // namespace quic
320