| // 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 <memory> | |
| #include <string> | |
| #include <utility> | |
| #include "absl/base/macros.h" | |
| #include "quiche/quic/core/crypto/quic_decrypter.h" | |
| #include "quiche/quic/core/crypto/quic_encrypter.h" | |
| #include "quiche/quic/core/quic_error_codes.h" | |
| #include "quiche/quic/core/quic_packets.h" | |
| #include "quiche/quic/core/quic_server_id.h" | |
| #include "quiche/quic/core/quic_types.h" | |
| #include "quiche/quic/core/quic_utils.h" | |
| #include "quiche/quic/core/quic_versions.h" | |
| #include "quiche/quic/platform/api/quic_expect_bug.h" | |
| #include "quiche/quic/platform/api/quic_flags.h" | |
| #include "quiche/quic/platform/api/quic_test.h" | |
| #include "quiche/quic/test_tools/crypto_test_utils.h" | |
| #include "quiche/quic/test_tools/quic_connection_peer.h" | |
| #include "quiche/quic/test_tools/quic_framer_peer.h" | |
| #include "quiche/quic/test_tools/quic_session_peer.h" | |
| #include "quiche/quic/test_tools/quic_test_utils.h" | |
| #include "quiche/quic/test_tools/simple_session_cache.h" | |
| #include "quiche/quic/tools/fake_proof_verifier.h" | |
| #include "quiche/common/test_tools/quiche_test_utils.h" | |
| using testing::_; | |
| namespace quic { | |
| namespace test { | |
| namespace { | |
| constexpr char kServerHostname[] = "test.example.com"; | |
| constexpr uint16_t kServerPort = 443; | |
| // TestProofVerifier wraps ProofVerifierForTesting, except for VerifyCertChain | |
| // which, if TestProofVerifier is active, always returns QUIC_PENDING. (If this | |
| // test proof verifier is not active, it delegates VerifyCertChain to the | |
| // ProofVerifierForTesting.) The pending VerifyCertChain operation can be | |
| // completed by calling InvokePendingCallback. This allows for testing | |
| // asynchronous VerifyCertChain operations. | |
| class TestProofVerifier : public ProofVerifier { | |
| public: | |
| TestProofVerifier() | |
| : verifier_(crypto_test_utils::ProofVerifierForTesting()) {} | |
| QuicAsyncStatus VerifyProof( | |
| const std::string& hostname, const uint16_t port, | |
| const std::string& server_config, QuicTransportVersion quic_version, | |
| absl::string_view chlo_hash, const std::vector<std::string>& certs, | |
| const std::string& cert_sct, const std::string& signature, | |
| const ProofVerifyContext* context, std::string* error_details, | |
| std::unique_ptr<ProofVerifyDetails>* details, | |
| std::unique_ptr<ProofVerifierCallback> callback) override { | |
| return verifier_->VerifyProof( | |
| hostname, port, server_config, quic_version, chlo_hash, certs, cert_sct, | |
| signature, context, error_details, details, std::move(callback)); | |
| } | |
| QuicAsyncStatus VerifyCertChain( | |
| const std::string& hostname, const uint16_t port, | |
| const std::vector<std::string>& certs, const std::string& ocsp_response, | |
| const std::string& cert_sct, const ProofVerifyContext* context, | |
| std::string* error_details, std::unique_ptr<ProofVerifyDetails>* details, | |
| uint8_t* out_alert, | |
| std::unique_ptr<ProofVerifierCallback> callback) override { | |
| if (!active_) { | |
| return verifier_->VerifyCertChain( | |
| hostname, port, certs, ocsp_response, cert_sct, context, | |
| error_details, details, out_alert, std::move(callback)); | |
| } | |
| pending_ops_.push_back(std::make_unique<VerifyChainPendingOp>( | |
| hostname, port, certs, ocsp_response, cert_sct, context, error_details, | |
| details, out_alert, std::move(callback), verifier_.get())); | |
| return QUIC_PENDING; | |
| } | |
| std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override { | |
| return nullptr; | |
| } | |
| void Activate() { active_ = true; } | |
| size_t NumPendingCallbacks() const { return pending_ops_.size(); } | |
| void InvokePendingCallback(size_t n) { | |
| ASSERT_GT(NumPendingCallbacks(), n); | |
| pending_ops_[n]->Run(); | |
| auto it = pending_ops_.begin() + n; | |
| pending_ops_.erase(it); | |
| } | |
| private: | |
| // Implementation of ProofVerifierCallback that fails if the callback is ever | |
| // run. | |
| class FailingProofVerifierCallback : public ProofVerifierCallback { | |
| public: | |
| void Run(bool /*ok*/, const std::string& /*error_details*/, | |
| std::unique_ptr<ProofVerifyDetails>* /*details*/) override { | |
| FAIL(); | |
| } | |
| }; | |
| class VerifyChainPendingOp { | |
| public: | |
| VerifyChainPendingOp(const std::string& hostname, const uint16_t port, | |
| const std::vector<std::string>& certs, | |
| const std::string& ocsp_response, | |
| const std::string& cert_sct, | |
| const ProofVerifyContext* context, | |
| std::string* error_details, | |
| std::unique_ptr<ProofVerifyDetails>* details, | |
| uint8_t* out_alert, | |
| std::unique_ptr<ProofVerifierCallback> callback, | |
| ProofVerifier* delegate) | |
| : hostname_(hostname), | |
| port_(port), | |
| certs_(certs), | |
| ocsp_response_(ocsp_response), | |
| cert_sct_(cert_sct), | |
| context_(context), | |
| error_details_(error_details), | |
| details_(details), | |
| out_alert_(out_alert), | |
| callback_(std::move(callback)), | |
| delegate_(delegate) {} | |
| void Run() { | |
| // TestProofVerifier depends on crypto_test_utils::ProofVerifierForTesting | |
| // running synchronously. It passes a FailingProofVerifierCallback and | |
| // runs the original callback after asserting that the verification ran | |
| // synchronously. | |
| QuicAsyncStatus status = delegate_->VerifyCertChain( | |
| hostname_, port_, certs_, ocsp_response_, cert_sct_, context_, | |
| error_details_, details_, out_alert_, | |
| std::make_unique<FailingProofVerifierCallback>()); | |
| ASSERT_NE(status, QUIC_PENDING); | |
| callback_->Run(status == QUIC_SUCCESS, *error_details_, details_); | |
| } | |
| private: | |
| std::string hostname_; | |
| const uint16_t port_; | |
| std::vector<std::string> certs_; | |
| std::string ocsp_response_; | |
| std::string cert_sct_; | |
| const ProofVerifyContext* context_; | |
| std::string* error_details_; | |
| std::unique_ptr<ProofVerifyDetails>* details_; | |
| uint8_t* out_alert_; | |
| std::unique_ptr<ProofVerifierCallback> callback_; | |
| ProofVerifier* delegate_; | |
| }; | |
| std::unique_ptr<ProofVerifier> verifier_; | |
| bool active_ = false; | |
| std::vector<std::unique_ptr<VerifyChainPendingOp>> pending_ops_; | |
| }; | |
| class TlsClientHandshakerTest : public QuicTestWithParam<ParsedQuicVersion> { | |
| public: | |
| TlsClientHandshakerTest() | |
| : supported_versions_({GetParam()}), | |
| server_id_(kServerHostname, kServerPort, false), | |
| server_compressed_certs_cache_( | |
| QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) { | |
| crypto_config_ = std::make_unique<QuicCryptoClientConfig>( | |
| std::make_unique<TestProofVerifier>(), | |
| std::make_unique<test::SimpleSessionCache>()); | |
| server_crypto_config_ = crypto_test_utils::CryptoServerConfigForTesting(); | |
| CreateConnection(); | |
| } | |
| void CreateSession() { | |
| session_ = std::make_unique<TestQuicSpdyClientSession>( | |
| connection_, DefaultQuicConfig(), supported_versions_, server_id_, | |
| crypto_config_.get()); | |
| EXPECT_CALL(*session_, GetAlpnsToOffer()) | |
| .WillRepeatedly(testing::Return(std::vector<std::string>( | |
| {AlpnForVersion(connection_->version())}))); | |
| } | |
| void CreateConnection() { | |
| connection_ = | |
| new PacketSavingConnection(&client_helper_, &alarm_factory_, | |
| Perspective::IS_CLIENT, supported_versions_); | |
| // Advance the time, because timers do not like uninitialized times. | |
| connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); | |
| CreateSession(); | |
| } | |
| void CompleteCryptoHandshake() { | |
| CompleteCryptoHandshakeWithServerALPN( | |
| AlpnForVersion(connection_->version())); | |
| } | |
| void CompleteCryptoHandshakeWithServerALPN(const std::string& alpn) { | |
| EXPECT_CALL(*connection_, SendCryptoData(_, _, _)) | |
| .Times(testing::AnyNumber()); | |
| stream()->CryptoConnect(); | |
| QuicConfig config; | |
| crypto_test_utils::HandshakeWithFakeServer( | |
| &config, server_crypto_config_.get(), &server_helper_, &alarm_factory_, | |
| connection_, stream(), alpn); | |
| } | |
| QuicCryptoClientStream* stream() { | |
| return session_->GetMutableCryptoStream(); | |
| } | |
| QuicCryptoServerStreamBase* server_stream() { | |
| return server_session_->GetMutableCryptoStream(); | |
| } | |
| // Initializes a fake server, and all its associated state, for testing. | |
| void InitializeFakeServer() { | |
| TestQuicSpdyServerSession* server_session = nullptr; | |
| CreateServerSessionForTest( | |
| server_id_, QuicTime::Delta::FromSeconds(100000), supported_versions_, | |
| &server_helper_, &alarm_factory_, server_crypto_config_.get(), | |
| &server_compressed_certs_cache_, &server_connection_, &server_session); | |
| server_session_.reset(server_session); | |
| std::string alpn = AlpnForVersion(connection_->version()); | |
| EXPECT_CALL(*server_session_, SelectAlpn(_)) | |
| .WillRepeatedly([alpn](const std::vector<absl::string_view>& alpns) { | |
| return std::find(alpns.cbegin(), alpns.cend(), alpn); | |
| }); | |
| } | |
| MockQuicConnectionHelper server_helper_; | |
| MockQuicConnectionHelper client_helper_; | |
| MockAlarmFactory alarm_factory_; | |
| PacketSavingConnection* connection_; | |
| ParsedQuicVersionVector supported_versions_; | |
| std::unique_ptr<TestQuicSpdyClientSession> session_; | |
| QuicServerId server_id_; | |
| CryptoHandshakeMessage message_; | |
| std::unique_ptr<QuicCryptoClientConfig> crypto_config_; | |
| // Server state. | |
| std::unique_ptr<QuicCryptoServerConfig> server_crypto_config_; | |
| PacketSavingConnection* server_connection_; | |
| std::unique_ptr<TestQuicSpdyServerSession> server_session_; | |
| QuicCompressedCertsCache server_compressed_certs_cache_; | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(TlsHandshakerTests, TlsClientHandshakerTest, | |
| ::testing::ValuesIn(AllSupportedVersionsWithTls()), | |
| ::testing::PrintToStringParamName()); | |
| TEST_P(TlsClientHandshakerTest, NotInitiallyConnected) { | |
| EXPECT_FALSE(stream()->encryption_established()); | |
| EXPECT_FALSE(stream()->one_rtt_keys_available()); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ConnectedAfterHandshake) { | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ConnectionClosedOnTlsError) { | |
| // Have client send ClientHello. | |
| stream()->CryptoConnect(); | |
| EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _, _)); | |
| // Send a zero-length ServerHello from server to client. | |
| char bogus_handshake_message[] = { | |
| // Handshake struct (RFC 8446 appendix B.3) | |
| 2, // HandshakeType server_hello | |
| 0, 0, 0, // uint24 length | |
| }; | |
| stream()->crypto_message_parser()->ProcessInput( | |
| absl::string_view(bogus_handshake_message, | |
| ABSL_ARRAYSIZE(bogus_handshake_message)), | |
| ENCRYPTION_INITIAL); | |
| EXPECT_FALSE(stream()->one_rtt_keys_available()); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ProofVerifyDetailsAvailableAfterHandshake) { | |
| EXPECT_CALL(*session_, OnProofVerifyDetailsAvailable(testing::_)); | |
| stream()->CryptoConnect(); | |
| QuicConfig config; | |
| crypto_test_utils::HandshakeWithFakeServer( | |
| &config, server_crypto_config_.get(), &server_helper_, &alarm_factory_, | |
| connection_, stream(), AlpnForVersion(connection_->version())); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| } | |
| TEST_P(TlsClientHandshakerTest, HandshakeWithAsyncProofVerifier) { | |
| InitializeFakeServer(); | |
| // Enable TestProofVerifier to capture call to VerifyCertChain and run it | |
| // asynchronously. | |
| TestProofVerifier* proof_verifier = | |
| static_cast<TestProofVerifier*>(crypto_config_->proof_verifier()); | |
| proof_verifier->Activate(); | |
| stream()->CryptoConnect(); | |
| // Exchange handshake messages. | |
| std::pair<size_t, size_t> moved_message_counts = | |
| crypto_test_utils::AdvanceHandshake( | |
| connection_, stream(), 0, server_connection_, server_stream(), 0); | |
| ASSERT_EQ(proof_verifier->NumPendingCallbacks(), 1u); | |
| proof_verifier->InvokePendingCallback(0); | |
| // Exchange more handshake messages. | |
| crypto_test_utils::AdvanceHandshake( | |
| connection_, stream(), moved_message_counts.first, server_connection_, | |
| server_stream(), moved_message_counts.second); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| } | |
| TEST_P(TlsClientHandshakerTest, Resumption) { | |
| // Disable 0-RTT on the server so that we're only testing 1-RTT resumption: | |
| SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); | |
| // Finish establishing the first connection: | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| // Create a second connection | |
| CreateConnection(); | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_TRUE(stream()->IsResumption()); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ResumptionRejection) { | |
| // Disable 0-RTT on the server before the first connection so the client | |
| // doesn't attempt a 0-RTT resumption, only a 1-RTT resumption. | |
| SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); | |
| // Finish establishing the first connection: | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| // Create a second connection, but disable resumption on the server. | |
| SSL_CTX_set_options(server_crypto_config_->ssl_ctx(), SSL_OP_NO_TICKET); | |
| CreateConnection(); | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| EXPECT_FALSE(stream()->EarlyDataAccepted()); | |
| EXPECT_EQ(stream()->EarlyDataReason(), | |
| ssl_early_data_unsupported_for_session); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ZeroRttResumption) { | |
| // Finish establishing the first connection: | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| // Create a second connection | |
| CreateConnection(); | |
| // OnConfigNegotiated should be called twice - once when processing saved | |
| // 0-RTT transport parameters, and then again when receiving transport | |
| // parameters from the server. | |
| EXPECT_CALL(*session_, OnConfigNegotiated()).Times(2); | |
| EXPECT_CALL(*connection_, SendCryptoData(_, _, _)) | |
| .Times(testing::AnyNumber()); | |
| // Start the second handshake and confirm we have keys before receiving any | |
| // messages from the server. | |
| stream()->CryptoConnect(); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_NE(stream()->crypto_negotiated_params().cipher_suite, 0); | |
| EXPECT_NE(stream()->crypto_negotiated_params().key_exchange_group, 0); | |
| EXPECT_NE(stream()->crypto_negotiated_params().peer_signature_algorithm, 0); | |
| // Finish the handshake with the server. | |
| QuicConfig config; | |
| crypto_test_utils::HandshakeWithFakeServer( | |
| &config, server_crypto_config_.get(), &server_helper_, &alarm_factory_, | |
| connection_, stream(), AlpnForVersion(connection_->version())); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_TRUE(stream()->IsResumption()); | |
| EXPECT_TRUE(stream()->EarlyDataAccepted()); | |
| EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_accepted); | |
| } | |
| // Regression test for b/186438140. | |
| TEST_P(TlsClientHandshakerTest, ZeroRttResumptionWithAyncProofVerifier) { | |
| // Finish establishing the first connection, so the second connection can | |
| // resume. | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| // Create a second connection. | |
| CreateConnection(); | |
| InitializeFakeServer(); | |
| EXPECT_CALL(*session_, OnConfigNegotiated()); | |
| EXPECT_CALL(*connection_, SendCryptoData(_, _, _)) | |
| .Times(testing::AnyNumber()); | |
| // Enable TestProofVerifier to capture the call to VerifyCertChain and run it | |
| // asynchronously. | |
| TestProofVerifier* proof_verifier = | |
| static_cast<TestProofVerifier*>(crypto_config_->proof_verifier()); | |
| proof_verifier->Activate(); | |
| // Start the second handshake. | |
| stream()->CryptoConnect(); | |
| ASSERT_EQ(proof_verifier->NumPendingCallbacks(), 1u); | |
| // Advance the handshake with the server. Since cert verification has not | |
| // finished yet, client cannot derive HANDSHAKE and 1-RTT keys. | |
| crypto_test_utils::AdvanceHandshake(connection_, stream(), 0, | |
| server_connection_, server_stream(), 0); | |
| EXPECT_FALSE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(server_stream()->one_rtt_keys_available()); | |
| // Finish cert verification after receiving packets from server. | |
| proof_verifier->InvokePendingCallback(0); | |
| QuicFramer* framer = QuicConnectionPeer::GetFramer(connection_); | |
| // Verify client has derived HANDSHAKE key. | |
| EXPECT_NE(nullptr, | |
| QuicFramerPeer::GetEncrypter(framer, ENCRYPTION_HANDSHAKE)); | |
| // Ideally, we should also verify that the process_undecryptable_packets_alarm | |
| // is set and processing the undecryptable packets can advance the handshake | |
| // to completion. Unfortunately, the test facilities used in this test does | |
| // not support queuing and processing undecryptable packets. | |
| } | |
| TEST_P(TlsClientHandshakerTest, ZeroRttRejection) { | |
| // Finish establishing the first connection: | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| // Create a second connection, but disable 0-RTT on the server. | |
| SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); | |
| CreateConnection(); | |
| // OnConfigNegotiated should be called twice - once when processing saved | |
| // 0-RTT transport parameters, and then again when receiving transport | |
| // parameters from the server. | |
| EXPECT_CALL(*session_, OnConfigNegotiated()).Times(2); | |
| // 4 packets will be sent in this connection: initial handshake packet, 0-RTT | |
| // packet containing SETTINGS, handshake packet upon 0-RTT rejection, 0-RTT | |
| // packet retransmission. | |
| EXPECT_CALL(*connection_, | |
| OnPacketSent(ENCRYPTION_INITIAL, NOT_RETRANSMISSION)); | |
| if (VersionUsesHttp3(session_->transport_version())) { | |
| EXPECT_CALL(*connection_, | |
| OnPacketSent(ENCRYPTION_ZERO_RTT, NOT_RETRANSMISSION)); | |
| } | |
| EXPECT_CALL(*connection_, | |
| OnPacketSent(ENCRYPTION_HANDSHAKE, NOT_RETRANSMISSION)); | |
| if (VersionUsesHttp3(session_->transport_version())) { | |
| // TODO(b/158027651): change transmission type to | |
| // ALL_ZERO_RTT_RETRANSMISSION. | |
| EXPECT_CALL(*connection_, | |
| OnPacketSent(ENCRYPTION_FORWARD_SECURE, LOSS_RETRANSMISSION)); | |
| } | |
| CompleteCryptoHandshake(); | |
| QuicFramer* framer = QuicConnectionPeer::GetFramer(connection_); | |
| EXPECT_EQ(nullptr, QuicFramerPeer::GetEncrypter(framer, ENCRYPTION_ZERO_RTT)); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_TRUE(stream()->IsResumption()); | |
| EXPECT_FALSE(stream()->EarlyDataAccepted()); | |
| EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_peer_declined); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ZeroRttAndResumptionRejection) { | |
| // Finish establishing the first connection: | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| // Create a second connection, but disable resumption on the server. | |
| SSL_CTX_set_options(server_crypto_config_->ssl_ctx(), SSL_OP_NO_TICKET); | |
| CreateConnection(); | |
| // OnConfigNegotiated should be called twice - once when processing saved | |
| // 0-RTT transport parameters, and then again when receiving transport | |
| // parameters from the server. | |
| EXPECT_CALL(*session_, OnConfigNegotiated()).Times(2); | |
| // 4 packets will be sent in this connection: initial handshake packet, 0-RTT | |
| // packet containing SETTINGS, handshake packet upon 0-RTT rejection, 0-RTT | |
| // packet retransmission. | |
| EXPECT_CALL(*connection_, | |
| OnPacketSent(ENCRYPTION_INITIAL, NOT_RETRANSMISSION)); | |
| if (VersionUsesHttp3(session_->transport_version())) { | |
| EXPECT_CALL(*connection_, | |
| OnPacketSent(ENCRYPTION_ZERO_RTT, NOT_RETRANSMISSION)); | |
| } | |
| EXPECT_CALL(*connection_, | |
| OnPacketSent(ENCRYPTION_HANDSHAKE, NOT_RETRANSMISSION)); | |
| if (VersionUsesHttp3(session_->transport_version())) { | |
| // TODO(b/158027651): change transmission type to | |
| // ALL_ZERO_RTT_RETRANSMISSION. | |
| EXPECT_CALL(*connection_, | |
| OnPacketSent(ENCRYPTION_FORWARD_SECURE, LOSS_RETRANSMISSION)); | |
| } | |
| CompleteCryptoHandshake(); | |
| QuicFramer* framer = QuicConnectionPeer::GetFramer(connection_); | |
| EXPECT_EQ(nullptr, QuicFramerPeer::GetEncrypter(framer, ENCRYPTION_ZERO_RTT)); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| EXPECT_FALSE(stream()->EarlyDataAccepted()); | |
| EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_session_not_resumed); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ClientSendsNoSNI) { | |
| // Reconfigure client to sent an empty server hostname. The crypto config also | |
| // needs to be recreated to use a FakeProofVerifier since the server's cert | |
| // won't match the empty hostname. | |
| server_id_ = QuicServerId("", 443); | |
| crypto_config_.reset(new QuicCryptoClientConfig( | |
| std::make_unique<FakeProofVerifier>(), nullptr)); | |
| CreateConnection(); | |
| InitializeFakeServer(); | |
| stream()->CryptoConnect(); | |
| crypto_test_utils::CommunicateHandshakeMessages( | |
| connection_, stream(), server_connection_, server_stream()); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_EQ(server_stream()->crypto_negotiated_params().sni, ""); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ClientSendingTooManyALPNs) { | |
| std::string long_alpn(250, 'A'); | |
| EXPECT_QUIC_BUG( | |
| { | |
| EXPECT_CALL(*session_, GetAlpnsToOffer()) | |
| .WillOnce(testing::Return(std::vector<std::string>({ | |
| long_alpn + "1", | |
| long_alpn + "2", | |
| long_alpn + "3", | |
| long_alpn + "4", | |
| long_alpn + "5", | |
| long_alpn + "6", | |
| long_alpn + "7", | |
| long_alpn + "8", | |
| }))); | |
| stream()->CryptoConnect(); | |
| }, | |
| "Failed to set ALPN"); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ServerRequiresCustomALPN) { | |
| InitializeFakeServer(); | |
| const std::string kTestAlpn = "An ALPN That Client Did Not Offer"; | |
| EXPECT_CALL(*server_session_, SelectAlpn(_)) | |
| .WillOnce([kTestAlpn](const std::vector<absl::string_view>& alpns) { | |
| return std::find(alpns.cbegin(), alpns.cend(), kTestAlpn); | |
| }); | |
| EXPECT_CALL(*server_connection_, | |
| CloseConnection(QUIC_HANDSHAKE_FAILED, | |
| static_cast<QuicIetfTransportErrorCodes>( | |
| CRYPTO_ERROR_FIRST + 120), | |
| "TLS handshake failure (ENCRYPTION_INITIAL) 120: " | |
| "no application protocol", | |
| _)); | |
| stream()->CryptoConnect(); | |
| crypto_test_utils::AdvanceHandshake(connection_, stream(), 0, | |
| server_connection_, server_stream(), 0); | |
| EXPECT_FALSE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(server_stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->encryption_established()); | |
| EXPECT_FALSE(server_stream()->encryption_established()); | |
| } | |
| TEST_P(TlsClientHandshakerTest, ZeroRTTNotAttemptedOnALPNChange) { | |
| // Finish establishing the first connection: | |
| CompleteCryptoHandshake(); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->IsResumption()); | |
| // Create a second connection | |
| CreateConnection(); | |
| // Override the ALPN to send on the second connection. | |
| const std::string kTestAlpn = "Test ALPN"; | |
| EXPECT_CALL(*session_, GetAlpnsToOffer()) | |
| .WillRepeatedly(testing::Return(std::vector<std::string>({kTestAlpn}))); | |
| // OnConfigNegotiated should only be called once: when transport parameters | |
| // are received from the server. | |
| EXPECT_CALL(*session_, OnConfigNegotiated()).Times(1); | |
| CompleteCryptoHandshakeWithServerALPN(kTestAlpn); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(stream()->EarlyDataAccepted()); | |
| EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_alpn_mismatch); | |
| } | |
| TEST_P(TlsClientHandshakerTest, InvalidSNI) { | |
| // Test that a client will skip sending SNI if configured to send an invalid | |
| // hostname. In this case, the inclusion of '!' is invalid. | |
| server_id_ = QuicServerId("invalid!.example.com", 443); | |
| crypto_config_.reset(new QuicCryptoClientConfig( | |
| std::make_unique<FakeProofVerifier>(), nullptr)); | |
| CreateConnection(); | |
| InitializeFakeServer(); | |
| stream()->CryptoConnect(); | |
| crypto_test_utils::CommunicateHandshakeMessages( | |
| connection_, stream(), server_connection_, server_stream()); | |
| EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); | |
| EXPECT_TRUE(stream()->encryption_established()); | |
| EXPECT_TRUE(stream()->one_rtt_keys_available()); | |
| EXPECT_EQ(server_stream()->crypto_negotiated_params().sni, ""); | |
| } | |
| TEST_P(TlsClientHandshakerTest, BadTransportParams) { | |
| if (!connection_->version().UsesHttp3()) { | |
| return; | |
| } | |
| // Finish establishing the first connection: | |
| CompleteCryptoHandshake(); | |
| // Create a second connection | |
| CreateConnection(); | |
| stream()->CryptoConnect(); | |
| auto* id_manager = QuicSessionPeer::ietf_streamid_manager(session_.get()); | |
| EXPECT_EQ(kDefaultMaxStreamsPerConnection, | |
| id_manager->max_outgoing_bidirectional_streams()); | |
| QuicConfig config; | |
| config.SetMaxBidirectionalStreamsToSend( | |
| config.GetMaxBidirectionalStreamsToSend() - 1); | |
| EXPECT_CALL(*connection_, | |
| CloseConnection(QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED, _, _)) | |
| .WillOnce(testing::Invoke(connection_, | |
| &MockQuicConnection::ReallyCloseConnection)); | |
| // Close connection will be called again in the handshaker, but this will be | |
| // no-op as the connection is already closed. | |
| EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _)); | |
| crypto_test_utils::HandshakeWithFakeServer( | |
| &config, server_crypto_config_.get(), &server_helper_, &alarm_factory_, | |
| connection_, stream(), AlpnForVersion(connection_->version())); | |
| } | |
| } // namespace | |
| } // namespace test | |
| } // namespace quic |