No public description PiperOrigin-RevId: 568864507
diff --git a/quiche/quic/core/quic_buffered_packet_store.cc b/quiche/quic/core/quic_buffered_packet_store.cc index df028f0..650a08f 100644 --- a/quiche/quic/core/quic_buffered_packet_store.cc +++ b/quiche/quic/core/quic_buffered_packet_store.cc
@@ -292,9 +292,11 @@ bool QuicBufferedPacketStore::IngestPacketForTlsChloExtraction( const QuicConnectionId& connection_id, const ParsedQuicVersion& version, - const QuicReceivedPacket& packet, std::vector<std::string>* out_alpns, - std::string* out_sni, bool* out_resumption_attempted, - bool* out_early_data_attempted, absl::optional<uint8_t>* tls_alert) { + const QuicReceivedPacket& packet, + std::vector<uint16_t>* out_supported_groups, + std::vector<std::string>* out_alpns, std::string* out_sni, + bool* out_resumption_attempted, bool* out_early_data_attempted, + absl::optional<uint8_t>* tls_alert) { QUICHE_DCHECK_NE(out_alpns, nullptr); QUICHE_DCHECK_NE(out_sni, nullptr); QUICHE_DCHECK_NE(tls_alert, nullptr); @@ -311,6 +313,7 @@ return false; } const TlsChloExtractor& tls_chlo_extractor = it->second.tls_chlo_extractor; + *out_supported_groups = tls_chlo_extractor.supported_groups(); *out_alpns = tls_chlo_extractor.alpns(); *out_sni = tls_chlo_extractor.server_name(); *out_resumption_attempted = tls_chlo_extractor.resumption_attempted();
diff --git a/quiche/quic/core/quic_buffered_packet_store.h b/quiche/quic/core/quic_buffered_packet_store.h index 734980f..0973d5c 100644 --- a/quiche/quic/core/quic_buffered_packet_store.h +++ b/quiche/quic/core/quic_buffered_packet_store.h
@@ -114,18 +114,21 @@ // Ingests this packet into the corresponding TlsChloExtractor. This should // only be called when HasBufferedPackets(connection_id) is true. // Returns whether we've now parsed a full multi-packet TLS CHLO. - // When this returns true, |out_alpns| is populated with the list of ALPNs - // extracted from the CHLO. |out_sni| is populated with the SNI tag in CHLO. - // |out_resumption_attempted| is populated if the CHLO has the - // 'pre_shared_key' TLS extension. |out_early_data_attempted| is populated if - // the CHLO has the 'early_data' TLS extension. - // When this returns false, and an unrecoverable error happened due to a TLS - // alert, |*tls_alert| will be set to the alert value. + // When this returns true, |out_supported_groups| is populated with the list + // of groups in the CHLO's 'supported_groups' TLS extension. |out_alpns| is + // populated with the list of ALPNs extracted from the CHLO. |out_sni| is + // populated with the SNI tag in CHLO. |out_resumption_attempted| is populated + // if the CHLO has the 'pre_shared_key' TLS extension. + // |out_early_data_attempted| is populated if the CHLO has the 'early_data' + // TLS extension. When this returns false, and an unrecoverable error happened + // due to a TLS alert, |*tls_alert| will be set to the alert value. bool IngestPacketForTlsChloExtraction( const QuicConnectionId& connection_id, const ParsedQuicVersion& version, - const QuicReceivedPacket& packet, std::vector<std::string>* out_alpns, - std::string* out_sni, bool* out_resumption_attempted, - bool* out_early_data_attempted, absl::optional<uint8_t>* tls_alert); + const QuicReceivedPacket& packet, + std::vector<uint16_t>* out_supported_groups, + std::vector<std::string>* out_alpns, std::string* out_sni, + bool* out_resumption_attempted, bool* out_early_data_attempted, + absl::optional<uint8_t>* tls_alert); // Returns the list of buffered packets for |connection_id| and removes them // from the store. Returns an empty list if no early arrived packets for this
diff --git a/quiche/quic/core/quic_buffered_packet_store_test.cc b/quiche/quic/core/quic_buffered_packet_store_test.cc index 3b32300..562b516 100644 --- a/quiche/quic/core/quic_buffered_packet_store_test.cc +++ b/quiche/quic/core/quic_buffered_packet_store_test.cc
@@ -463,6 +463,7 @@ TEST_F(QuicBufferedPacketStoreTest, IngestPacketForTlsChloExtraction) { QuicConnectionId connection_id = TestConnectionId(1); std::vector<std::string> alpns; + std::vector<uint16_t> supported_groups; std::string sni; bool resumption_attempted = false; bool early_data_attempted = false; @@ -476,7 +477,7 @@ // The packet in 'packet_' is not a TLS CHLO packet. EXPECT_FALSE(store_.IngestPacketForTlsChloExtraction( - connection_id, valid_version_, packet_, &alpns, &sni, + connection_id, valid_version_, packet_, &supported_groups, &alpns, &sni, &resumption_attempted, &early_data_attempted, &tls_alert)); store_.DiscardPackets(connection_id); @@ -497,13 +498,18 @@ EXPECT_TRUE(store_.HasBufferedPackets(connection_id)); EXPECT_FALSE(store_.IngestPacketForTlsChloExtraction( - connection_id, valid_version_, *packets[0], &alpns, &sni, - &resumption_attempted, &early_data_attempted, &tls_alert)); + connection_id, valid_version_, *packets[0], &supported_groups, &alpns, + &sni, &resumption_attempted, &early_data_attempted, &tls_alert)); EXPECT_TRUE(store_.IngestPacketForTlsChloExtraction( - connection_id, valid_version_, *packets[1], &alpns, &sni, - &resumption_attempted, &early_data_attempted, &tls_alert)); + connection_id, valid_version_, *packets[1], &supported_groups, &alpns, + &sni, &resumption_attempted, &early_data_attempted, &tls_alert)); EXPECT_THAT(alpns, ElementsAre(AlpnForVersion(valid_version_))); + if (GetQuicReloadableFlag(quic_extract_supported_groups_early)) { + EXPECT_FALSE(supported_groups.empty()); + } else { + EXPECT_TRUE(supported_groups.empty()); + } EXPECT_EQ(sni, TestHostname()); EXPECT_FALSE(resumption_attempted);
diff --git a/quiche/quic/core/quic_dispatcher.cc b/quiche/quic/core/quic_dispatcher.cc index bdc339a..09ece44 100644 --- a/quiche/quic/core/quic_dispatcher.cc +++ b/quiche/quic/core/quic_dispatcher.cc
@@ -678,6 +678,7 @@ if (packet_info.version.UsesTls()) { bool has_full_tls_chlo = false; std::string sni; + std::vector<uint16_t> supported_groups; std::vector<std::string> alpns; bool resumption_attempted = false, early_data_attempted = false; if (buffered_packets_.HasBufferedPackets( @@ -686,8 +687,8 @@ // use the associated TlsChloExtractor to parse this packet. has_full_tls_chlo = buffered_packets_.IngestPacketForTlsChloExtraction( packet_info.destination_connection_id, packet_info.version, - packet_info.packet, &alpns, &sni, &resumption_attempted, - &early_data_attempted, &result.tls_alert); + packet_info.packet, &supported_groups, &alpns, &sni, + &resumption_attempted, &early_data_attempted, &result.tls_alert); } else { // If we do not have a BufferedPacketList for this connection ID, // create a single-use one to check whether this packet contains a @@ -697,6 +698,7 @@ if (tls_chlo_extractor.HasParsedFullChlo()) { // This packet contains a full single-packet CHLO. has_full_tls_chlo = true; + supported_groups = tls_chlo_extractor.supported_groups(); alpns = tls_chlo_extractor.alpns(); sni = tls_chlo_extractor.server_name(); resumption_attempted = tls_chlo_extractor.resumption_attempted(); @@ -723,6 +725,7 @@ ParsedClientHello& parsed_chlo = result.parsed_chlo.emplace(); parsed_chlo.sni = std::move(sni); + parsed_chlo.supported_groups = std::move(supported_groups); parsed_chlo.alpns = std::move(alpns); if (packet_info.retry_token.has_value()) { parsed_chlo.retry_token = std::string(*packet_info.retry_token);
diff --git a/quiche/quic/core/quic_dispatcher_test.cc b/quiche/quic/core/quic_dispatcher_test.cc index 6cd5390..35b96ff 100644 --- a/quiche/quic/core/quic_dispatcher_test.cc +++ b/quiche/quic/core/quic_dispatcher_test.cc
@@ -64,11 +64,16 @@ #include "quiche/common/test_tools/quiche_test_utils.h" using testing::_; +using testing::AllOf; using testing::ByMove; +using testing::ElementsAreArray; using testing::Eq; +using testing::Field; using testing::InSequence; using testing::Invoke; +using testing::IsEmpty; using testing::NiceMock; +using testing::Not; using testing::Ref; using testing::Return; using testing::ReturnRef; @@ -517,11 +522,18 @@ std::string ExpectedAlpn() { return ExpectedAlpnForVersion(version_); } - ParsedClientHello ParsedClientHelloForTest() { - ParsedClientHello parsed_chlo; - parsed_chlo.alpns = {ExpectedAlpn()}; - parsed_chlo.sni = TestHostname(); - return parsed_chlo; + auto MatchParsedClientHello() { + if (version_.UsesQuicCrypto() || + !GetQuicReloadableFlag(quic_extract_supported_groups_early)) { + return AllOf( + Field(&ParsedClientHello::alpns, ElementsAreArray({ExpectedAlpn()})), + Field(&ParsedClientHello::sni, Eq(TestHostname())), + Field(&ParsedClientHello::supported_groups, IsEmpty())); + } + return AllOf( + Field(&ParsedClientHello::alpns, ElementsAreArray({ExpectedAlpn()})), + Field(&ParsedClientHello::sni, Eq(TestHostname())), + Field(&ParsedClientHello::supported_groups, Not(IsEmpty()))); } void MarkSession1Deleted() { session1_ = nullptr; } @@ -612,10 +624,10 @@ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, - CreateQuicSession(TestConnectionId(1), _, client_address, - Eq(ExpectedAlpn()), _, - Eq(ParsedClientHelloForTest()), _)) + EXPECT_CALL( + *dispatcher_, + CreateQuicSession(TestConnectionId(1), _, client_address, + Eq(ExpectedAlpn()), _, MatchParsedClientHello(), _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -643,9 +655,9 @@ ConnectionIdGeneratorInterface& expected_generator = mock_connection_id_generator; EXPECT_CALL(*dispatcher_, - CreateQuicSession( - TestConnectionId(1), _, client_address, Eq(ExpectedAlpn()), _, - Eq(ParsedClientHelloForTest()), Ref(expected_generator))) + CreateQuicSession(TestConnectionId(1), _, client_address, + Eq(ExpectedAlpn()), _, MatchParsedClientHello(), + Ref(expected_generator))) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -667,7 +679,7 @@ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); EXPECT_CALL(*dispatcher_, CreateQuicSession(new_id, _, client_address, Eq(ExpectedAlpn()), - _, Eq(ParsedClientHelloForTest()), _)) + _, MatchParsedClientHello(), _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, new_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -732,10 +744,10 @@ << "No session should be created before the rest of the CHLO arrives."; // Processing the second packet should create the new session. - EXPECT_CALL(*dispatcher_, - CreateQuicSession(new_connection_id, _, client_address, - Eq(ExpectedAlpn()), _, - Eq(ParsedClientHelloForTest()), _)) + EXPECT_CALL( + *dispatcher_, + CreateQuicSession(new_connection_id, _, client_address, + Eq(ExpectedAlpn()), _, MatchParsedClientHello(), _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, new_connection_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -773,10 +785,10 @@ TEST_P(QuicDispatcherTestAllVersions, ProcessPackets) { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, - CreateQuicSession(TestConnectionId(1), _, client_address, - Eq(ExpectedAlpn()), _, - Eq(ParsedClientHelloForTest()), _)) + EXPECT_CALL( + *dispatcher_, + CreateQuicSession(TestConnectionId(1), _, client_address, + Eq(ExpectedAlpn()), _, MatchParsedClientHello(), _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -788,10 +800,10 @@ }))); ProcessFirstFlight(client_address, TestConnectionId(1)); - EXPECT_CALL(*dispatcher_, - CreateQuicSession(TestConnectionId(2), _, client_address, - Eq(ExpectedAlpn()), _, - Eq(ParsedClientHelloForTest()), _)) + EXPECT_CALL( + *dispatcher_, + CreateQuicSession(TestConnectionId(2), _, client_address, + Eq(ExpectedAlpn()), _, MatchParsedClientHello(), _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(2), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -2553,7 +2565,7 @@ .WillOnce(Return(absl::nullopt)); EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_addr_, Eq(ExpectedAlpn()), _, - Eq(ParsedClientHelloForTest()), _)) + MatchParsedClientHello(), _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -2754,10 +2766,10 @@ EXPECT_CALL(connection_id_generator_, MaybeReplaceConnectionId(TestConnectionId(conn_id), version_)) .WillOnce(Return(absl::nullopt)); - EXPECT_CALL(*dispatcher_, - CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, - Eq(ExpectedAlpn()), _, - Eq(ParsedClientHelloForTest()), _)) + EXPECT_CALL( + *dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, + Eq(ExpectedAlpn()), _, MatchParsedClientHello(), _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, &mock_helper_, &mock_alarm_factory_, @@ -2794,10 +2806,10 @@ EXPECT_CALL(connection_id_generator_, MaybeReplaceConnectionId(TestConnectionId(conn_id), version_)) .WillOnce(Return(absl::nullopt)); - EXPECT_CALL(*dispatcher_, - CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, - Eq(ExpectedAlpn()), _, - Eq(ParsedClientHelloForTest()), _)) + EXPECT_CALL( + *dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, + Eq(ExpectedAlpn()), _, MatchParsedClientHello(), _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, &mock_helper_, &mock_alarm_factory_, &crypto_config_,
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h index 1d11804..87b37ef 100644 --- a/quiche/quic/core/quic_flags_list.h +++ b/quiche/quic/core/quic_flags_list.h
@@ -25,6 +25,8 @@ QUIC_FLAG(quic_reloadable_flag_quic_ignore_gquic_probing, true) // If true, QUIC will default enable MTU discovery at server, with a target of 1450 bytes. QUIC_FLAG(quic_reloadable_flag_quic_enable_mtu_discovery_at_server, false) +// If true, QUIC will extract supported_groups from ClientHello before creating QuicSession. +QUIC_FLAG(quic_reloadable_flag_quic_extract_supported_groups_early, true) // If true, QuicGsoBatchWriter will support release time if it is available and the process has the permission to do so. QUIC_FLAG(quic_restart_flag_quic_support_release_time_for_gso, false) // If true, a duplicate NEW_CID frame will be ignore during QUIC packet processing.
diff --git a/quiche/quic/core/quic_types.cc b/quiche/quic/core/quic_types.cc index 168d699..b7b2b26 100644 --- a/quiche/quic/core/quic_types.cc +++ b/quiche/quic/core/quic_types.cc
@@ -417,7 +417,8 @@ } bool operator==(const ParsedClientHello& a, const ParsedClientHello& b) { - return a.sni == b.sni && a.uaid == b.uaid && a.alpns == b.alpns && + return a.sni == b.sni && a.uaid == b.uaid && + a.supported_groups == b.supported_groups && a.alpns == b.alpns && a.retry_token == b.retry_token && a.resumption_attempted == b.resumption_attempted && a.early_data_attempted == b.early_data_attempted; @@ -427,6 +428,10 @@ const ParsedClientHello& parsed_chlo) { os << "{ sni:" << parsed_chlo.sni << ", uaid:" << parsed_chlo.uaid << ", alpns:" << quiche::PrintElements(parsed_chlo.alpns) + << ", supported_groups:" + << quiche::PrintElements(parsed_chlo.supported_groups) + << ", resumption_attempted:" << parsed_chlo.resumption_attempted + << ", early_data_attempted:" << parsed_chlo.early_data_attempted << ", len(retry_token):" << parsed_chlo.retry_token.size() << " }"; return os; }
diff --git a/quiche/quic/core/quic_types.h b/quiche/quic/core/quic_types.h index e58d0d6..727eb73 100644 --- a/quiche/quic/core/quic_types.h +++ b/quiche/quic/core/quic_types.h
@@ -861,9 +861,10 @@ // ParsedClientHello contains client hello information extracted from a fully // received client hello. struct QUICHE_EXPORT ParsedClientHello { - std::string sni; // QUIC crypto and TLS. - std::string uaid; // QUIC crypto only. - std::vector<std::string> alpns; // QUIC crypto and TLS. + std::string sni; // QUIC crypto and TLS. + std::string uaid; // QUIC crypto only. + std::vector<uint16_t> supported_groups; // TLS only. + std::vector<std::string> alpns; // QUIC crypto and TLS. // The unvalidated retry token from the last received packet of a potentially // multi-packet client hello. TLS only. std::string retry_token;
diff --git a/quiche/quic/core/tls_chlo_extractor.cc b/quiche/quic/core/tls_chlo_extractor.cc index 9a741d0..7cfcf25 100644 --- a/quiche/quic/core/tls_chlo_extractor.cc +++ b/quiche/quic/core/tls_chlo_extractor.cc
@@ -4,8 +4,10 @@ #include "quiche/quic/core/tls_chlo_extractor.h" +#include <cstdint> #include <cstring> #include <memory> +#include <vector> #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -18,6 +20,7 @@ #include "quiche/quic/core/quic_types.h" #include "quiche/quic/core/quic_versions.h" #include "quiche/quic/platform/api/quic_bug_tracker.h" +#include "quiche/quic/platform/api/quic_flags.h" #include "quiche/common/platform/api/quiche_logging.h" namespace quic { @@ -30,6 +33,41 @@ &unused_extension_bytes, &unused_extension_len); } + +std::vector<uint16_t> GetSupportedGroups(const SSL_CLIENT_HELLO* client_hello) { + const uint8_t* extension_data; + size_t extension_len; + int rv = SSL_early_callback_ctx_extension_get( + client_hello, TLSEXT_TYPE_supported_groups, &extension_data, + &extension_len); + if (rv != 1) { + return {}; + } + + // See https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.7 for the + // format of this extension. + QuicDataReader named_groups_reader( + reinterpret_cast<const char*>(extension_data), extension_len); + uint16_t named_groups_len; + if (!named_groups_reader.ReadUInt16(&named_groups_len) || + named_groups_len + sizeof(uint16_t) != extension_len) { + QUIC_CODE_COUNT(quic_chlo_supported_groups_invalid_length); + return {}; + } + + std::vector<uint16_t> named_groups; + while (!named_groups_reader.IsDoneReading()) { + uint16_t named_group; + if (!named_groups_reader.ReadUInt16(&named_group)) { + QUIC_CODE_COUNT(quic_chlo_supported_groups_odd_length); + QUIC_LOG_FIRST_N(WARNING, 10) << "Failed to read named groups"; + break; + } + named_groups.push_back(named_group); + } + return named_groups; +} + } // namespace TlsChloExtractor::TlsChloExtractor() @@ -60,6 +98,7 @@ error_details_ = std::move(other.error_details_); parsed_crypto_frame_in_this_packet_ = other.parsed_crypto_frame_in_this_packet_; + supported_groups_ = std::move(other.supported_groups_); alpns_ = std::move(other.alpns_); server_name_ = std::move(other.server_name_); client_hello_bytes_ = std::move(other.client_hello_bytes_); @@ -323,6 +362,11 @@ } } + if (GetQuicReloadableFlag(quic_extract_supported_groups_early)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_extract_supported_groups_early, 1, 3); + supported_groups_ = GetSupportedGroups(client_hello); + } + // Update our state now that we've parsed a full CHLO. if (state_ == State::kInitial) { state_ = State::kParsedFullSinglePacketChlo;
diff --git a/quiche/quic/core/tls_chlo_extractor.h b/quiche/quic/core/tls_chlo_extractor.h index 55b0142..324365e 100644 --- a/quiche/quic/core/tls_chlo_extractor.h +++ b/quiche/quic/core/tls_chlo_extractor.h
@@ -5,6 +5,7 @@ #ifndef QUICHE_QUIC_CORE_TLS_CHLO_EXTRACTOR_H_ #define QUICHE_QUIC_CORE_TLS_CHLO_EXTRACTOR_H_ +#include <cstdint> #include <memory> #include <string> #include <vector> @@ -49,6 +50,9 @@ std::string server_name() const { return server_name_; } bool resumption_attempted() const { return resumption_attempted_; } bool early_data_attempted() const { return early_data_attempted_; } + const std::vector<uint16_t>& supported_groups() const { + return supported_groups_; + } absl::Span<const uint8_t> client_hello_bytes() const { return client_hello_bytes_; } @@ -253,6 +257,8 @@ std::string error_details_; // Whether a CRYPTO frame was parsed in this packet. bool parsed_crypto_frame_in_this_packet_; + // Array of NamedGroups parsed from the CHLO's supported_groups extension. + std::vector<uint16_t> supported_groups_; // Array of ALPNs parsed from the CHLO. std::vector<std::string> alpns_; // SNI parsed from the CHLO.
diff --git a/quiche/quic/core/tls_chlo_extractor_test.cc b/quiche/quic/core/tls_chlo_extractor_test.cc index 44a1e63..34fe798 100644 --- a/quiche/quic/core/tls_chlo_extractor_test.cc +++ b/quiche/quic/core/tls_chlo_extractor_test.cc
@@ -29,18 +29,26 @@ TlsChloExtractorTest() : version_(GetParam()), server_id_(TestServerId()) {} void Initialize() { + tls_chlo_extractor_ = std::make_unique<TlsChloExtractor>(); AnnotatedPackets packets = GetAnnotatedFirstFlightOfPackets(version_, config_); packets_ = std::move(packets.packets); crypto_stream_size_ = packets.crypto_stream_size; + QUIC_DLOG(INFO) << "Initialized with " << packets_.size() + << " packets with crypto_stream_size:" + << crypto_stream_size_; } void Initialize(std::unique_ptr<QuicCryptoClientConfig> crypto_config) { + tls_chlo_extractor_ = std::make_unique<TlsChloExtractor>(); AnnotatedPackets packets = GetAnnotatedFirstFlightOfPackets( version_, config_, TestConnectionId(), EmptyQuicConnectionId(), std::move(crypto_config)); packets_ = std::move(packets.packets); crypto_stream_size_ = packets.crypto_stream_size; + QUIC_DLOG(INFO) << "Initialized with " << packets_.size() + << " packets with crypto_stream_size:" + << crypto_stream_size_; } // Perform a full handshake in order to insert a SSL_SESSION into @@ -107,14 +115,15 @@ &packet_info.destination_connection_id, &packet_info.source_connection_id, &retry_token, &detailed_error); ASSERT_THAT(error, IsQuicNoError()) << detailed_error; - tls_chlo_extractor_.IngestPacket(packet_info.version, packet_info.packet); + tls_chlo_extractor_->IngestPacket(packet_info.version, + packet_info.packet); } packets_.clear(); } void ValidateChloDetails(const TlsChloExtractor* extractor = nullptr) const { if (extractor == nullptr) { - extractor = &tls_chlo_extractor_; + extractor = tls_chlo_extractor_.get(); } EXPECT_TRUE(extractor->HasParsedFullChlo()); @@ -147,7 +156,7 @@ ParsedQuicVersion version_; QuicServerId server_id_; - TlsChloExtractor tls_chlo_extractor_; + std::unique_ptr<TlsChloExtractor> tls_chlo_extractor_; QuicConfig config_; std::vector<std::unique_ptr<QuicReceivedPacket>> packets_; uint64_t crypto_stream_size_; @@ -162,13 +171,13 @@ EXPECT_EQ(packets_.size(), 1u); IngestPackets(); ValidateChloDetails(); - EXPECT_EQ(tls_chlo_extractor_.state(), + EXPECT_EQ(tls_chlo_extractor_->state(), TlsChloExtractor::State::kParsedFullSinglePacketChlo); - EXPECT_FALSE(tls_chlo_extractor_.resumption_attempted()); - EXPECT_FALSE(tls_chlo_extractor_.early_data_attempted()); + EXPECT_FALSE(tls_chlo_extractor_->resumption_attempted()); + EXPECT_FALSE(tls_chlo_extractor_->early_data_attempted()); } -TEST_P(TlsChloExtractorTest, TlsExtentionInfo_ResumptionOnly) { +TEST_P(TlsChloExtractorTest, TlsExtensionInfo_ResumptionOnly) { auto crypto_client_config = std::make_unique<QuicCryptoClientConfig>( crypto_test_utils::ProofVerifierForTesting(), std::make_unique<SimpleSessionCache>()); @@ -179,13 +188,13 @@ EXPECT_GE(packets_.size(), 1u); IngestPackets(); ValidateChloDetails(); - EXPECT_EQ(tls_chlo_extractor_.state(), + EXPECT_EQ(tls_chlo_extractor_->state(), TlsChloExtractor::State::kParsedFullSinglePacketChlo); - EXPECT_TRUE(tls_chlo_extractor_.resumption_attempted()); - EXPECT_FALSE(tls_chlo_extractor_.early_data_attempted()); + EXPECT_TRUE(tls_chlo_extractor_->resumption_attempted()); + EXPECT_FALSE(tls_chlo_extractor_->early_data_attempted()); } -TEST_P(TlsChloExtractorTest, TlsExtentionInfo_ZeroRtt) { +TEST_P(TlsChloExtractorTest, TlsExtensionInfo_ZeroRtt) { auto crypto_client_config = std::make_unique<QuicCryptoClientConfig>( crypto_test_utils::ProofVerifierForTesting(), std::make_unique<SimpleSessionCache>()); @@ -196,10 +205,34 @@ EXPECT_GE(packets_.size(), 1u); IngestPackets(); ValidateChloDetails(); - EXPECT_EQ(tls_chlo_extractor_.state(), + EXPECT_EQ(tls_chlo_extractor_->state(), TlsChloExtractor::State::kParsedFullMultiPacketChlo); - EXPECT_TRUE(tls_chlo_extractor_.resumption_attempted()); - EXPECT_TRUE(tls_chlo_extractor_.early_data_attempted()); + EXPECT_TRUE(tls_chlo_extractor_->resumption_attempted()); + EXPECT_TRUE(tls_chlo_extractor_->early_data_attempted()); +} + +TEST_P(TlsChloExtractorTest, TlsExtensionInfo_SupportedGroups) { + const std::vector<std::vector<uint16_t>> preferred_groups_to_test = { + // Only one group + {SSL_GROUP_X25519}, + // Two groups + {SSL_GROUP_X25519_KYBER768_DRAFT00, SSL_GROUP_X25519}, + }; + for (const std::vector<uint16_t>& preferred_groups : + preferred_groups_to_test) { + auto crypto_client_config = std::make_unique<QuicCryptoClientConfig>( + crypto_test_utils::ProofVerifierForTesting()); + crypto_client_config->set_preferred_groups(preferred_groups); + + Initialize(std::move(crypto_client_config)); + IngestPackets(); + ValidateChloDetails(); + if (GetQuicReloadableFlag(quic_extract_supported_groups_early)) { + EXPECT_EQ(tls_chlo_extractor_->supported_groups(), preferred_groups); + } else { + EXPECT_TRUE(tls_chlo_extractor_->supported_groups().empty()); + } + } } TEST_P(TlsChloExtractorTest, MultiPacket) { @@ -208,7 +241,7 @@ EXPECT_EQ(packets_.size(), 2u); IngestPackets(); ValidateChloDetails(); - EXPECT_EQ(tls_chlo_extractor_.state(), + EXPECT_EQ(tls_chlo_extractor_->state(), TlsChloExtractor::State::kParsedFullMultiPacketChlo); } @@ -216,11 +249,11 @@ IncreaseSizeOfChlo(); Initialize(); ASSERT_EQ(packets_.size(), 2u); - // Artifically reorder both packets. + // Artificially reorder both packets. std::swap(packets_[0], packets_[1]); IngestPackets(); ValidateChloDetails(); - EXPECT_EQ(tls_chlo_extractor_.state(), + EXPECT_EQ(tls_chlo_extractor_->state(), TlsChloExtractor::State::kParsedFullMultiPacketChlo); } @@ -228,10 +261,10 @@ Initialize(); EXPECT_EQ(packets_.size(), 1u); TlsChloExtractor other_extractor; - tls_chlo_extractor_ = std::move(other_extractor); + *tls_chlo_extractor_ = std::move(other_extractor); IngestPackets(); ValidateChloDetails(); - EXPECT_EQ(tls_chlo_extractor_.state(), + EXPECT_EQ(tls_chlo_extractor_->state(), TlsChloExtractor::State::kParsedFullSinglePacketChlo); } @@ -240,10 +273,10 @@ EXPECT_EQ(packets_.size(), 1u); IngestPackets(); ValidateChloDetails(); - EXPECT_EQ(tls_chlo_extractor_.state(), + EXPECT_EQ(tls_chlo_extractor_->state(), TlsChloExtractor::State::kParsedFullSinglePacketChlo); - TlsChloExtractor other_extractor = std::move(tls_chlo_extractor_); + TlsChloExtractor other_extractor = std::move(*tls_chlo_extractor_); EXPECT_EQ(other_extractor.state(), TlsChloExtractor::State::kParsedFullSinglePacketChlo); @@ -276,13 +309,13 @@ EXPECT_EQ(packets_.size(), 1u); // Move the extractor. - tls_chlo_extractor_ = std::move(other_extractor); + *tls_chlo_extractor_ = std::move(other_extractor); // Have |tls_chlo_extractor_| parse the second packet. IngestPackets(); ValidateChloDetails(); - EXPECT_EQ(tls_chlo_extractor_.state(), + EXPECT_EQ(tls_chlo_extractor_->state(), TlsChloExtractor::State::kParsedFullMultiPacketChlo); }