xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/tls_chlo_extractor_test.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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