Project import generated by Copybara.
PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/quic_crypto_server_stream_test.cc b/quic/core/quic_crypto_server_stream_test.cc
new file mode 100644
index 0000000..660c588
--- /dev/null
+++ b/quic/core/quic_crypto_server_stream_test.cc
@@ -0,0 +1,573 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/failing_proof_source.h"
+#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+class QuicConnection;
+class QuicStream;
+} // namespace quic
+
+using testing::_;
+using testing::NiceMock;
+
+namespace quic {
+namespace test {
+
+class QuicCryptoServerStreamPeer {
+ public:
+ static bool DoesPeerSupportStatelessRejects(
+ const CryptoHandshakeMessage& message) {
+ return QuicCryptoServerStream::DoesPeerSupportStatelessRejects(message);
+ }
+};
+
+namespace {
+
+const char kServerHostname[] = "test.example.com";
+const uint16_t kServerPort = 443;
+
+class QuicCryptoServerStreamTest : public QuicTestWithParam<bool> {
+ public:
+ QuicCryptoServerStreamTest()
+ : QuicCryptoServerStreamTest(crypto_test_utils::ProofSourceForTesting()) {
+ }
+
+ explicit QuicCryptoServerStreamTest(std::unique_ptr<ProofSource> proof_source)
+ : server_crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ std::move(proof_source),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ server_compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ server_id_(kServerHostname, kServerPort, false),
+ client_crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx()) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, false);
+ }
+
+ void Initialize() { InitializeServer(); }
+
+ ~QuicCryptoServerStreamTest() override {
+ // Ensure that anything that might reference |helpers_| is destroyed before
+ // |helpers_| is destroyed.
+ server_session_.reset();
+ client_session_.reset();
+ helpers_.clear();
+ alarm_factories_.clear();
+ }
+
+ // Initializes the crypto server stream state for testing. May be
+ // called multiple times.
+ void InitializeServer() {
+ TestQuicSpdyServerSession* server_session = nullptr;
+ helpers_.push_back(QuicMakeUnique<NiceMock<MockQuicConnectionHelper>>());
+ alarm_factories_.push_back(QuicMakeUnique<MockAlarmFactory>());
+ CreateServerSessionForTest(
+ server_id_, QuicTime::Delta::FromSeconds(100000), supported_versions_,
+ helpers_.back().get(), alarm_factories_.back().get(),
+ &server_crypto_config_, &server_compressed_certs_cache_,
+ &server_connection_, &server_session);
+ CHECK(server_session);
+ server_session_.reset(server_session);
+ EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*server_session_->helper(), GenerateConnectionIdForReject(_, _))
+ .Times(testing::AnyNumber());
+ crypto_test_utils::FakeServerOptions options;
+ options.token_binding_params = QuicTagVector{kTB10};
+ crypto_test_utils::SetupCryptoServerConfigForTest(
+ server_connection_->clock(), server_connection_->random_generator(),
+ &server_crypto_config_, options);
+ server_session_->GetMutableCryptoStream()->OnSuccessfulVersionNegotiation(
+ supported_versions_.front());
+ }
+
+ QuicCryptoServerStream* server_stream() {
+ return server_session_->GetMutableCryptoStream();
+ }
+
+ QuicCryptoClientStream* client_stream() {
+ return client_session_->GetMutableCryptoStream();
+ }
+
+ // Initializes a fake client, and all its associated state, for
+ // testing. May be called multiple times.
+ void InitializeFakeClient(bool supports_stateless_rejects) {
+ TestQuicSpdyClientSession* client_session = nullptr;
+ helpers_.push_back(QuicMakeUnique<NiceMock<MockQuicConnectionHelper>>());
+ alarm_factories_.push_back(QuicMakeUnique<MockAlarmFactory>());
+ CreateClientSessionForTest(
+ server_id_, supports_stateless_rejects,
+ QuicTime::Delta::FromSeconds(100000), supported_versions_,
+ helpers_.back().get(), alarm_factories_.back().get(),
+ &client_crypto_config_, &client_connection_, &client_session);
+ CHECK(client_session);
+ client_session_.reset(client_session);
+ }
+
+ int CompleteCryptoHandshake() {
+ CHECK(server_connection_);
+ CHECK(server_session_ != nullptr);
+
+ return crypto_test_utils::HandshakeWithFakeClient(
+ helpers_.back().get(), alarm_factories_.back().get(),
+ server_connection_, server_stream(), server_id_, client_options_);
+ }
+
+ // Performs a single round of handshake message-exchange between the
+ // client and server.
+ void AdvanceHandshakeWithFakeClient() {
+ CHECK(server_connection_);
+ CHECK(client_session_ != nullptr);
+
+ EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*client_session_, OnProofVerifyDetailsAvailable(_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*client_connection_, OnCanWrite()).Times(testing::AnyNumber());
+ EXPECT_CALL(*server_connection_, OnCanWrite()).Times(testing::AnyNumber());
+ client_stream()->CryptoConnect();
+ crypto_test_utils::AdvanceHandshake(client_connection_, client_stream(), 0,
+ server_connection_, server_stream(), 0);
+ }
+
+ protected:
+ // Every connection gets its own MockQuicConnectionHelper and
+ // MockAlarmFactory, tracked separately from the server and client state so
+ // their lifetimes persist through the whole test.
+ std::vector<std::unique_ptr<MockQuicConnectionHelper>> helpers_;
+ std::vector<std::unique_ptr<MockAlarmFactory>> alarm_factories_;
+
+ // Server state.
+ PacketSavingConnection* server_connection_;
+ std::unique_ptr<TestQuicSpdyServerSession> server_session_;
+ QuicCryptoServerConfig server_crypto_config_;
+ QuicCompressedCertsCache server_compressed_certs_cache_;
+ QuicServerId server_id_;
+
+ // Client state.
+ PacketSavingConnection* client_connection_;
+ QuicCryptoClientConfig client_crypto_config_;
+ std::unique_ptr<TestQuicSpdyClientSession> client_session_;
+
+ CryptoHandshakeMessage message_;
+ crypto_test_utils::FakeClientOptions client_options_;
+
+ // Which QUIC versions the client and server support.
+ ParsedQuicVersionVector supported_versions_ = AllSupportedVersions();
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests, QuicCryptoServerStreamTest, testing::Bool());
+
+TEST_P(QuicCryptoServerStreamTest, NotInitiallyConected) {
+ Initialize();
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, NotInitiallySendingStatelessRejects) {
+ Initialize();
+ EXPECT_FALSE(server_stream()->UseStatelessRejectsIfPeerSupported());
+ EXPECT_FALSE(server_stream()->PeerSupportsStatelessRejects());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ConnectedAfterCHLO) {
+ // CompleteCryptoHandshake returns the number of client hellos sent. This
+ // test should send:
+ // * One to get a source-address token and certificates.
+ // * One to complete the handshake.
+ Initialize();
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ConnectedAfterTlsHandshake) {
+ FLAGS_quic_supports_tls_handshake = true;
+ client_options_.only_tls_versions = true;
+ supported_versions_.clear();
+ for (QuicTransportVersion transport_version :
+ AllSupportedTransportVersions()) {
+ supported_versions_.push_back(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, transport_version));
+ }
+ Initialize();
+ CompleteCryptoHandshake();
+ EXPECT_EQ(PROTOCOL_TLS1_3, server_stream()->handshake_protocol());
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ForwardSecureAfterCHLO) {
+ Initialize();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ // Do a first handshake in order to prime the client config with the server's
+ // information.
+ AdvanceHandshakeWithFakeClient();
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+
+ // Now do another handshake, with the blocking SHLO connection option.
+ InitializeServer();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ AdvanceHandshakeWithFakeClient();
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE,
+ server_session_->connection()->encryption_level());
+}
+
+TEST_P(QuicCryptoServerStreamTest, StatelessRejectAfterCHLO) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+ Initialize();
+
+ InitializeFakeClient(/* supports_stateless_rejects= */ true);
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ EXPECT_CALL(*client_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ AdvanceHandshakeWithFakeClient();
+
+ // Check the server to make the sure the handshake did not succeed.
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+
+ // Check the client state to make sure that it received a server-designated
+ // connection id.
+ QuicCryptoClientConfig::CachedState* client_state =
+ client_crypto_config_.LookupOrCreate(server_id_);
+
+ ASSERT_TRUE(client_state->has_server_nonce());
+ ASSERT_FALSE(client_state->GetNextServerNonce().empty());
+ ASSERT_FALSE(client_state->has_server_nonce());
+
+ ASSERT_TRUE(client_state->has_server_designated_connection_id());
+ const QuicConnectionId server_designated_connection_id =
+ client_state->GetNextServerDesignatedConnectionId();
+ const QuicConnectionId expected_id = QuicUtils::CreateRandomConnectionId(
+ server_connection_->random_generator());
+ EXPECT_EQ(expected_id, server_designated_connection_id);
+ EXPECT_FALSE(client_state->has_server_designated_connection_id());
+ ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+}
+
+TEST_P(QuicCryptoServerStreamTest, ConnectedAfterStatelessHandshake) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+ Initialize();
+
+ InitializeFakeClient(/* supports_stateless_rejects= */ true);
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ EXPECT_CALL(*client_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ AdvanceHandshakeWithFakeClient();
+
+ // On the first round, encryption will not be established.
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+ EXPECT_EQ(1, server_stream()->NumHandshakeMessages());
+ EXPECT_EQ(0, server_stream()->NumHandshakeMessagesWithServerNonces());
+
+ // Now check the client state.
+ QuicCryptoClientConfig::CachedState* client_state =
+ client_crypto_config_.LookupOrCreate(server_id_);
+
+ ASSERT_TRUE(client_state->has_server_designated_connection_id());
+ const QuicConnectionId server_designated_connection_id =
+ client_state->GetNextServerDesignatedConnectionId();
+ const QuicConnectionId expected_id = QuicUtils::CreateRandomConnectionId(
+ server_connection_->random_generator());
+ EXPECT_EQ(expected_id, server_designated_connection_id);
+ EXPECT_FALSE(client_state->has_server_designated_connection_id());
+ ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+
+ // Now create new client and server streams with the existing config
+ // and try the handshake again (0-RTT handshake).
+ InitializeServer();
+
+ InitializeFakeClient(/* supports_stateless_rejects= */ true);
+ // In the stateless case, the second handshake contains a server-nonce, so the
+ // AsyncStrikeRegisterVerification() case will still succeed (unlike a 0-RTT
+ // handshake).
+ AdvanceHandshakeWithFakeClient();
+
+ // On the second round, encryption will be established.
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+ EXPECT_EQ(1, server_stream()->NumHandshakeMessages());
+ EXPECT_EQ(1, server_stream()->NumHandshakeMessagesWithServerNonces());
+}
+
+TEST_P(QuicCryptoServerStreamTest, NoStatelessRejectIfNoClientSupport) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+ Initialize();
+
+ // The server is configured to use stateless rejects, but the client does not
+ // support it.
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+ AdvanceHandshakeWithFakeClient();
+
+ // Check the server to make the sure the handshake did not succeed.
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+
+ // Check the client state to make sure that it did not receive a
+ // server-designated connection id.
+ QuicCryptoClientConfig::CachedState* client_state =
+ client_crypto_config_.LookupOrCreate(server_id_);
+
+ ASSERT_FALSE(client_state->has_server_designated_connection_id());
+ ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+}
+
+TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
+ Initialize();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ // Do a first handshake in order to prime the client config with the server's
+ // information.
+ AdvanceHandshakeWithFakeClient();
+ EXPECT_FALSE(server_stream()->ZeroRttAttempted());
+
+ // Now do another handshake, hopefully in 0-RTT.
+ QUIC_LOG(INFO) << "Resetting for 0-RTT handshake attempt";
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+ InitializeServer();
+
+ EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*client_session_, OnProofVerifyDetailsAvailable(_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*client_connection_, OnCanWrite()).Times(testing::AnyNumber());
+ client_stream()->CryptoConnect();
+
+ EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*client_session_, OnProofVerifyDetailsAvailable(_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*client_connection_, OnCanWrite()).Times(testing::AnyNumber());
+ crypto_test_utils::CommunicateHandshakeMessages(
+ client_connection_, client_stream(), server_connection_, server_stream());
+
+ EXPECT_EQ(1, client_stream()->num_sent_client_hellos());
+ EXPECT_TRUE(server_stream()->ZeroRttAttempted());
+}
+
+TEST_P(QuicCryptoServerStreamTest, FailByPolicy) {
+ Initialize();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
+ .WillOnce(testing::Return(false));
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_HANDSHAKE_FAILED, _, _));
+
+ AdvanceHandshakeWithFakeClient();
+}
+
+TEST_P(QuicCryptoServerStreamTest, MessageAfterHandshake) {
+ Initialize();
+ CompleteCryptoHandshake();
+ EXPECT_CALL(
+ *server_connection_,
+ CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, _, _));
+ message_.set_tag(kCHLO);
+ crypto_test_utils::SendHandshakeMessageToStream(server_stream(), message_,
+ Perspective::IS_CLIENT);
+}
+
+TEST_P(QuicCryptoServerStreamTest, BadMessageType) {
+ Initialize();
+
+ message_.set_tag(kSHLO);
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, _, _));
+ crypto_test_utils::SendHandshakeMessageToStream(server_stream(), message_,
+ Perspective::IS_SERVER);
+}
+
+TEST_P(QuicCryptoServerStreamTest, ChannelID) {
+ Initialize();
+
+ client_options_.channel_id_enabled = true;
+ client_options_.channel_id_source_async = false;
+ // CompleteCryptoHandshake verifies
+ // server_stream()->crypto_negotiated_params().channel_id is correct.
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ChannelIDAsync) {
+ Initialize();
+
+ client_options_.channel_id_enabled = true;
+ client_options_.channel_id_source_async = true;
+ // CompleteCryptoHandshake verifies
+ // server_stream()->crypto_negotiated_params().channel_id is correct.
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, OnlySendSCUPAfterHandshakeComplete) {
+ // An attempt to send a SCUP before completing handshake should fail.
+ Initialize();
+
+ server_stream()->SendServerConfigUpdate(nullptr);
+ EXPECT_EQ(0, server_stream()->NumServerConfigUpdateMessagesSent());
+}
+
+TEST_P(QuicCryptoServerStreamTest, SendSCUPAfterHandshakeComplete) {
+ Initialize();
+
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ // Do a first handshake in order to prime the client config with the server's
+ // information.
+ AdvanceHandshakeWithFakeClient();
+
+ // Now do another handshake, with the blocking SHLO connection option.
+ InitializeServer();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+ AdvanceHandshakeWithFakeClient();
+
+ // Send a SCUP message and ensure that the client was able to verify it.
+ EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0);
+ server_stream()->SendServerConfigUpdate(nullptr);
+ crypto_test_utils::AdvanceHandshake(client_connection_, client_stream(), 1,
+ server_connection_, server_stream(), 1);
+
+ EXPECT_EQ(1, server_stream()->NumServerConfigUpdateMessagesSent());
+ EXPECT_EQ(1, client_stream()->num_scup_messages_received());
+}
+
+TEST_P(QuicCryptoServerStreamTest, DoesPeerSupportStatelessRejects) {
+ Initialize();
+
+ QuicConfig stateless_reject_config = DefaultQuicConfigStatelessRejects();
+ stateless_reject_config.ToHandshakeMessage(&message_);
+ EXPECT_TRUE(
+ QuicCryptoServerStreamPeer::DoesPeerSupportStatelessRejects(message_));
+
+ message_.Clear();
+ QuicConfig stateful_reject_config = DefaultQuicConfig();
+ stateful_reject_config.ToHandshakeMessage(&message_);
+ EXPECT_FALSE(
+ QuicCryptoServerStreamPeer::DoesPeerSupportStatelessRejects(message_));
+}
+
+class QuicCryptoServerStreamTestWithFailingProofSource
+ : public QuicCryptoServerStreamTest {
+ public:
+ QuicCryptoServerStreamTestWithFailingProofSource()
+ : QuicCryptoServerStreamTest(
+ std::unique_ptr<FailingProofSource>(new FailingProofSource)) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(MoreTests,
+ QuicCryptoServerStreamTestWithFailingProofSource,
+ testing::Bool());
+
+TEST_P(QuicCryptoServerStreamTestWithFailingProofSource, Test) {
+ Initialize();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "Failed to get proof", _));
+ // Regression test for b/31521252, in which a crash would happen here.
+ AdvanceHandshakeWithFakeClient();
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+}
+
+class QuicCryptoServerStreamTestWithFakeProofSource
+ : public QuicCryptoServerStreamTest {
+ public:
+ QuicCryptoServerStreamTestWithFakeProofSource()
+ : QuicCryptoServerStreamTest(
+ std::unique_ptr<FakeProofSource>(new FakeProofSource)),
+ crypto_config_peer_(&server_crypto_config_) {}
+
+ FakeProofSource* GetFakeProofSource() const {
+ return static_cast<FakeProofSource*>(crypto_config_peer_.GetProofSource());
+ }
+
+ protected:
+ QuicCryptoServerConfigPeer crypto_config_peer_;
+};
+
+INSTANTIATE_TEST_SUITE_P(YetMoreTests,
+ QuicCryptoServerStreamTestWithFakeProofSource,
+ testing::Bool());
+
+// Regression test for b/35422225, in which multiple CHLOs arriving on the same
+// connection in close succession could cause a crash, especially when the use
+// of Mentat signing meant that it took a while for each CHLO to be processed.
+TEST_P(QuicCryptoServerStreamTestWithFakeProofSource, MultipleChlo) {
+ Initialize();
+ GetFakeProofSource()->Activate();
+ EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
+ .WillOnce(testing::Return(true));
+
+ // Create a minimal CHLO
+ MockClock clock;
+ QuicTransportVersion version = AllSupportedTransportVersions().front();
+ CryptoHandshakeMessage chlo = crypto_test_utils::GenerateDefaultInchoateCHLO(
+ &clock, version, &server_crypto_config_);
+
+ // Send in the CHLO, and check that a callback is now pending in the
+ // ProofSource.
+ crypto_test_utils::SendHandshakeMessageToStream(server_stream(), chlo,
+ Perspective::IS_CLIENT);
+ EXPECT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Send in a second CHLO while processing of the first is still pending.
+ // Verify that the server closes the connection rather than crashing. Note
+ // that the crash is a use-after-free, so it may only show up consistently in
+ // ASAN tests.
+ EXPECT_CALL(
+ *server_connection_,
+ CloseConnection(QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO,
+ "Unexpected handshake message while processing CHLO", _));
+ crypto_test_utils::SendHandshakeMessageToStream(server_stream(), chlo,
+ Perspective::IS_CLIENT);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic