| // 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 "quiche/quic/core/tls_server_handshaker.h" | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <iterator> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/base/attributes.h" | |
| #include "absl/base/macros.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "openssl/ssl.h" | |
| #include "quiche/quic/core/crypto/certificate_util.h" | |
| #include "quiche/quic/core/crypto/certificate_view.h" | |
| #include "quiche/quic/core/crypto/client_proof_source.h" | |
| #include "quiche/quic/core/crypto/proof_source.h" | |
| #include "quiche/quic/core/crypto/proof_verifier.h" | |
| #include "quiche/quic/core/crypto/quic_compressed_certs_cache.h" | |
| #include "quiche/quic/core/crypto/quic_crypto_client_config.h" | |
| #include "quiche/quic/core/crypto/quic_crypto_server_config.h" | |
| #include "quiche/quic/core/crypto/quic_random.h" | |
| #include "quiche/quic/core/crypto/transport_parameters.h" | |
| #include "quiche/quic/core/quic_crypto_client_stream.h" | |
| #include "quiche/quic/core/quic_error_codes.h" | |
| #include "quiche/quic/core/quic_session.h" | |
| #include "quiche/quic/core/quic_time.h" | |
| #include "quiche/quic/core/quic_types.h" | |
| #include "quiche/quic/core/quic_versions.h" | |
| #include "quiche/quic/core/tls_handshaker.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/failing_proof_source.h" | |
| #include "quiche/quic/test_tools/fake_proof_source.h" | |
| #include "quiche/quic/test_tools/fake_proof_source_handle.h" | |
| #include "quiche/quic/test_tools/quic_config_peer.h" | |
| #include "quiche/quic/test_tools/quic_test_utils.h" | |
| #include "quiche/quic/test_tools/simple_session_cache.h" | |
| #include "quiche/quic/test_tools/test_certificates.h" | |
| #include "quiche/quic/test_tools/test_ticket_crypter.h" | |
| #include "quiche/common/platform/api/quiche_logging.h" | |
| #include "quiche/common/platform/api/quiche_reference_counted.h" | |
| namespace quic { | |
| class QuicConnection; | |
| class QuicStream; | |
| } // namespace quic | |
| using testing::_; | |
| using testing::AllOf; | |
| using testing::ElementsAre; | |
| using testing::Eq; | |
| using testing::Field; | |
| using testing::HasSubstr; | |
| using testing::IsNull; | |
| using testing::NiceMock; | |
| using testing::Return; | |
| using testing::VariantWith; | |
| namespace quic { | |
| namespace test { | |
| class TlsServerHandshakerPeer { | |
| public: | |
| static const TransportParameters* GetServerParams( | |
| const TlsServerHandshaker* handshaker) { | |
| return handshaker->server_params_.get(); | |
| } | |
| }; | |
| namespace { | |
| const char kServerHostname[] = "test.example.com"; | |
| const uint16_t kServerPort = 443; | |
| struct TestParams { | |
| ParsedQuicVersion version; | |
| bool disable_resumption; | |
| }; | |
| [[maybe_unused]] // Used by ::testing::PrintToStringParamName(). | |
| std::string PrintToString(const TestParams& p) { | |
| return absl::StrCat(ParsedQuicVersionToString(p.version), "AndResumption", | |
| (p.disable_resumption ? "Disabled" : "Enabled")); | |
| } | |
| // Constructs test permutations. | |
| std::vector<TestParams> GetTestParams() { | |
| std::vector<TestParams> params; | |
| for (const auto& version : AllSupportedVersionsWithTls()) { | |
| for (bool disable_resumption : {false, true}) { | |
| params.push_back(TestParams{version, disable_resumption}); | |
| } | |
| } | |
| return params; | |
| } | |
| class MockProofVerifier : public ProofVerifier { | |
| public: | |
| MOCK_METHOD(QuicAsyncStatus, VerifyProof, | |
| (const std::string& hostname, uint16_t port, | |
| const std::string& server_config, | |
| QuicTransportVersion transport_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)); | |
| MOCK_METHOD(QuicAsyncStatus, VerifyCertChain, | |
| (const std::string& hostname, uint16_t port, | |
| const std::vector<absl::string_view>& 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)); | |
| MOCK_METHOD(std::unique_ptr<ProofVerifyContext>, CreateDefaultContext, (), | |
| (override)); | |
| }; | |
| class TestTlsServerHandshaker : public TlsServerHandshaker { | |
| public: | |
| static constexpr TransportParameters::TransportParameterId | |
| kFailHandshakeParam{0xFFEACA}; | |
| TestTlsServerHandshaker(QuicSession* session, | |
| const QuicCryptoServerConfig* crypto_config) | |
| : TlsServerHandshaker(session, crypto_config), | |
| proof_source_(crypto_config->proof_source()) { | |
| ON_CALL(*this, MaybeCreateProofSourceHandle()) | |
| .WillByDefault(testing::Invoke( | |
| this, &TestTlsServerHandshaker::RealMaybeCreateProofSourceHandle)); | |
| ON_CALL(*this, OverrideQuicConfigDefaults(_)) | |
| .WillByDefault(testing::Invoke( | |
| this, &TestTlsServerHandshaker::RealOverrideQuicConfigDefaults)); | |
| ON_CALL(*this, OnSelectCertificateDone) | |
| .WillByDefault(testing::Invoke( | |
| this, &TestTlsServerHandshaker::RealOnSelectCertificateDone)); | |
| } | |
| MOCK_METHOD(std::unique_ptr<ProofSourceHandle>, MaybeCreateProofSourceHandle, | |
| (), (override)); | |
| MOCK_METHOD(void, OverrideQuicConfigDefaults, (QuicConfig * config), | |
| (override)); | |
| MOCK_METHOD(void, OnSelectCertificateDone, | |
| (bool, bool, SSLConfig, absl::string_view, bool), (override)); | |
| MOCK_METHOD(void, InfoCallback, (int, int), (override)); | |
| void EnableInfoCallback() { tls_connection().EnableInfoCallback(); } | |
| // Makes the next call to `MaybeCreateProofSourceHandle()` return a | |
| // `FakeProofSourceHandle` instead of a real `ProofSourceHandle`. | |
| void SetupProofSourceHandle( | |
| FakeProofSourceHandle::Action select_cert_action, | |
| FakeProofSourceHandle::Action compute_signature_action, | |
| QuicDelayedSSLConfig dealyed_ssl_config = QuicDelayedSSLConfig()) { | |
| EXPECT_CALL(*this, MaybeCreateProofSourceHandle()) | |
| .WillOnce([this, select_cert_action, compute_signature_action, | |
| dealyed_ssl_config]() { | |
| auto handle = std::make_unique<FakeProofSourceHandle>( | |
| proof_source_, this, select_cert_action, compute_signature_action, | |
| dealyed_ssl_config); | |
| fake_proof_source_handle_ = handle.get(); | |
| return handle; | |
| }); | |
| } | |
| FakeProofSourceHandle* fake_proof_source_handle() { | |
| return fake_proof_source_handle_; | |
| } | |
| bool received_client_cert() const { return received_client_cert_; } | |
| QuicAsyncStatus verify_cert_chain_status() const { | |
| return verify_cert_chain_status_; | |
| } | |
| using TlsServerHandshaker::AdvanceHandshake; | |
| using TlsServerHandshaker::expected_ssl_error; | |
| protected: | |
| QuicAsyncStatus VerifyCertChain( | |
| const std::vector<absl::string_view>& certs, std::string* error_details, | |
| std::unique_ptr<ProofVerifyDetails>* details, uint8_t* out_alert, | |
| std::unique_ptr<ProofVerifierCallback> callback) override { | |
| received_client_cert_ = true; | |
| verify_cert_chain_status_ = TlsServerHandshaker::VerifyCertChain( | |
| certs, error_details, details, out_alert, std::move(callback)); | |
| return verify_cert_chain_status_; | |
| } | |
| bool ProcessAdditionalTransportParameters( | |
| const TransportParameters& params) override { | |
| return !params.custom_parameters.contains(kFailHandshakeParam); | |
| } | |
| private: | |
| std::unique_ptr<ProofSourceHandle> RealMaybeCreateProofSourceHandle() { | |
| return TlsServerHandshaker::MaybeCreateProofSourceHandle(); | |
| } | |
| void RealOverrideQuicConfigDefaults(QuicConfig* config) { | |
| return TlsServerHandshaker::OverrideQuicConfigDefaults(config); | |
| } | |
| void RealOnSelectCertificateDone(bool ok, bool is_sync, SSLConfig ssl_config, | |
| absl::string_view ticket_encryption_key, | |
| bool cert_matched_sni) { | |
| return TlsServerHandshaker::OnSelectCertificateDone( | |
| ok, is_sync, std::move(ssl_config), ticket_encryption_key, | |
| cert_matched_sni); | |
| } | |
| // Owned by TlsServerHandshaker. | |
| FakeProofSourceHandle* fake_proof_source_handle_ = nullptr; | |
| ProofSource* proof_source_ = nullptr; | |
| QuicAsyncStatus verify_cert_chain_status_; | |
| bool received_client_cert_ = false; | |
| }; | |
| class TlsServerHandshakerTestSession : public TestQuicSpdyServerSession { | |
| public: | |
| using TestQuicSpdyServerSession::TestQuicSpdyServerSession; | |
| std::unique_ptr<QuicCryptoServerStreamBase> CreateQuicCryptoServerStream( | |
| const QuicCryptoServerConfig* crypto_config, | |
| QuicCompressedCertsCache* /*compressed_certs_cache*/) override { | |
| if (connection()->version().IsIetfQuic()) { | |
| return std::make_unique<NiceMock<TestTlsServerHandshaker>>(this, | |
| crypto_config); | |
| } | |
| QUICHE_CHECK(false) << "Unsupported QUIC version: " | |
| << connection()->version().transport_version; | |
| return nullptr; | |
| } | |
| }; | |
| class TlsServerHandshakerTest : public QuicTestWithParam<TestParams> { | |
| public: | |
| TlsServerHandshakerTest() | |
| : server_compressed_certs_cache_( | |
| QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), | |
| server_id_(kServerHostname, kServerPort), | |
| supported_versions_({GetParam().version}) { | |
| SetQuicFlag(quic_disable_server_tls_resumption, | |
| GetParam().disable_resumption); | |
| client_crypto_config_ = std::make_unique<QuicCryptoClientConfig>( | |
| crypto_test_utils::ProofVerifierForTesting(), | |
| std::make_unique<test::SimpleSessionCache>()); | |
| InitializeServerConfig(); | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| } | |
| ~TlsServerHandshakerTest() 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(); | |
| } | |
| std::unique_ptr<FakeProofSource> InitializeProofSource() { | |
| auto ticket_crypter = std::make_unique<TestTicketCrypter>(); | |
| ticket_crypter_ = ticket_crypter.get(); | |
| auto proof_source = std::make_unique<FakeProofSource>(); | |
| proof_source_ = proof_source.get(); | |
| proof_source_->SetTicketCrypter(std::move(ticket_crypter)); | |
| return proof_source; | |
| } | |
| void InitializeServerConfig() { | |
| server_crypto_config_ = std::make_unique<QuicCryptoServerConfig>( | |
| QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(), | |
| InitializeProofSource(), KeyExchangeSource::Default()); | |
| } | |
| void InitializeServerConfigWithFailingProofSource() { | |
| server_crypto_config_ = std::make_unique<QuicCryptoServerConfig>( | |
| QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(), | |
| std::make_unique<FailingProofSource>(), KeyExchangeSource::Default()); | |
| } | |
| void InitializeServerConfigWithProofSourceAndVerifier( | |
| std::unique_ptr<ProofSource> proof_source, | |
| std::unique_ptr<ProofVerifier> proof_verifier) { | |
| server_crypto_config_ = std::make_unique<QuicCryptoServerConfig>( | |
| QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(), | |
| std::move(proof_source), KeyExchangeSource::Default(), | |
| std::move(proof_verifier)); | |
| } | |
| void CreateTlsServerHandshakerTestSession(MockQuicConnectionHelper* helper, | |
| MockAlarmFactory* alarm_factory) { | |
| server_connection_ = new PacketSavingConnection( | |
| helper, alarm_factory, Perspective::IS_SERVER, | |
| ParsedVersionOfIndex(supported_versions_, 0)); | |
| TlsServerHandshakerTestSession* server_session = | |
| new TlsServerHandshakerTestSession( | |
| server_connection_, DefaultQuicConfig(), supported_versions_, | |
| server_crypto_config_.get(), &server_compressed_certs_cache_); | |
| server_session->set_client_cert_mode(initial_client_cert_mode_); | |
| server_session->Initialize(); | |
| // We advance the clock initially because the default time is zero and the | |
| // strike register worries that we've just overflowed a uint32_t time. | |
| server_connection_->AdvanceTime(QuicTime::Delta::FromSeconds(100000)); | |
| QUICHE_CHECK(server_session); | |
| server_session_.reset(server_session); | |
| } | |
| void InitializeServerWithFakeProofSourceHandle() { | |
| helpers_.push_back(std::make_unique<NiceMock<MockQuicConnectionHelper>>()); | |
| alarm_factories_.push_back(std::make_unique<MockAlarmFactory>()); | |
| CreateTlsServerHandshakerTestSession(helpers_.back().get(), | |
| alarm_factories_.back().get()); | |
| server_handshaker_ = static_cast<NiceMock<TestTlsServerHandshaker>*>( | |
| server_session_->GetMutableCryptoStream()); | |
| EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _)) | |
| .Times(testing::AnyNumber()); | |
| EXPECT_CALL(*server_session_, SelectAlpn(_)) | |
| .WillRepeatedly([this](const std::vector<absl::string_view>& alpns) { | |
| return std::find( | |
| alpns.cbegin(), alpns.cend(), | |
| AlpnForVersion(server_session_->connection()->version())); | |
| }); | |
| crypto_test_utils::SetupCryptoServerConfigForTest( | |
| server_connection_->clock(), server_connection_->random_generator(), | |
| server_crypto_config_.get()); | |
| } | |
| // Initializes the crypto server stream state for testing. May be | |
| // called multiple times. | |
| void InitializeServer() { | |
| TestQuicSpdyServerSession* server_session = nullptr; | |
| helpers_.push_back(std::make_unique<NiceMock<MockQuicConnectionHelper>>()); | |
| alarm_factories_.push_back(std::make_unique<MockAlarmFactory>()); | |
| CreateServerSessionForTest( | |
| server_id_, QuicTime::Delta::FromSeconds(100000), supported_versions_, | |
| helpers_.back().get(), alarm_factories_.back().get(), | |
| server_crypto_config_.get(), &server_compressed_certs_cache_, | |
| &server_connection_, &server_session); | |
| QUICHE_CHECK(server_session); | |
| server_session_.reset(server_session); | |
| server_handshaker_ = nullptr; | |
| EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _)) | |
| .Times(testing::AnyNumber()); | |
| EXPECT_CALL(*server_session_, SelectAlpn(_)) | |
| .WillRepeatedly([this](const std::vector<absl::string_view>& alpns) { | |
| return std::find( | |
| alpns.cbegin(), alpns.cend(), | |
| AlpnForVersion(server_session_->connection()->version())); | |
| }); | |
| crypto_test_utils::SetupCryptoServerConfigForTest( | |
| server_connection_->clock(), server_connection_->random_generator(), | |
| server_crypto_config_.get()); | |
| } | |
| QuicCryptoServerStreamBase* server_stream() { | |
| return server_session_->GetMutableCryptoStream(); | |
| } | |
| QuicCryptoClientStream* client_stream() { | |
| return client_session_->GetMutableCryptoStream(); | |
| } | |
| SSL* server_ssl() { return server_stream()->GetSsl(); } | |
| // Initializes a fake client, and all its associated state, for | |
| // testing. May be called multiple times. | |
| void InitializeFakeClient() { | |
| TestQuicSpdyClientSession* client_session = nullptr; | |
| helpers_.push_back(std::make_unique<NiceMock<MockQuicConnectionHelper>>()); | |
| alarm_factories_.push_back(std::make_unique<MockAlarmFactory>()); | |
| CreateClientSessionForTest( | |
| server_id_, QuicTime::Delta::FromSeconds(100000), supported_versions_, | |
| helpers_.back().get(), alarm_factories_.back().get(), | |
| client_crypto_config_.get(), &client_connection_, &client_session); | |
| const std::string default_alpn = | |
| AlpnForVersion(client_connection_->version()); | |
| ON_CALL(*client_session, GetAlpnsToOffer()) | |
| .WillByDefault(Return(std::vector<std::string>({default_alpn}))); | |
| QUICHE_CHECK(client_session); | |
| client_session_.reset(client_session); | |
| moved_messages_counts_ = {0, 0}; | |
| } | |
| void CompleteCryptoHandshake() { | |
| while (!client_stream()->one_rtt_keys_available() || | |
| !server_stream()->one_rtt_keys_available()) { | |
| auto previous_moved_messages_counts = moved_messages_counts_; | |
| AdvanceHandshakeWithFakeClient(); | |
| // Check that the handshake has made forward progress | |
| ASSERT_NE(previous_moved_messages_counts, moved_messages_counts_); | |
| } | |
| } | |
| // Performs a single round of handshake message-exchange between the | |
| // client and server. | |
| void AdvanceHandshakeWithFakeClient() { | |
| QUICHE_CHECK(server_connection_); | |
| QUICHE_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()); | |
| // Call CryptoConnect if we haven't moved any client messages yet. | |
| if (moved_messages_counts_.first == 0) { | |
| client_stream()->CryptoConnect(); | |
| } | |
| moved_messages_counts_ = crypto_test_utils::AdvanceHandshake( | |
| client_connection_, client_stream(), moved_messages_counts_.first, | |
| server_connection_, server_stream(), moved_messages_counts_.second); | |
| } | |
| void ExpectHandshakeSuccessful() { | |
| EXPECT_TRUE(client_stream()->one_rtt_keys_available()); | |
| EXPECT_TRUE(client_stream()->encryption_established()); | |
| EXPECT_TRUE(server_stream()->one_rtt_keys_available()); | |
| EXPECT_TRUE(server_stream()->encryption_established()); | |
| EXPECT_EQ(HANDSHAKE_COMPLETE, client_stream()->GetHandshakeState()); | |
| EXPECT_EQ(HANDSHAKE_CONFIRMED, server_stream()->GetHandshakeState()); | |
| const auto& client_crypto_params = | |
| client_stream()->crypto_negotiated_params(); | |
| const auto& server_crypto_params = | |
| server_stream()->crypto_negotiated_params(); | |
| // The TLS params should be filled in on the client. | |
| EXPECT_NE(0, client_crypto_params.cipher_suite); | |
| EXPECT_NE(0, client_crypto_params.key_exchange_group); | |
| EXPECT_NE(0, client_crypto_params.peer_signature_algorithm); | |
| // The cipher suite and key exchange group should match on the client and | |
| // server. | |
| EXPECT_EQ(client_crypto_params.cipher_suite, | |
| server_crypto_params.cipher_suite); | |
| EXPECT_EQ(client_crypto_params.key_exchange_group, | |
| server_crypto_params.key_exchange_group); | |
| // We don't support client certs on the server (yet), so the server | |
| // shouldn't have a peer signature algorithm to report. | |
| EXPECT_EQ(0, server_crypto_params.peer_signature_algorithm); | |
| } | |
| // Should only be called when using FakeProofSourceHandle. | |
| FakeProofSourceHandle::SelectCertArgs last_select_cert_args() const { | |
| QUICHE_CHECK(server_handshaker_ && | |
| server_handshaker_->fake_proof_source_handle()); | |
| QUICHE_CHECK(!server_handshaker_->fake_proof_source_handle() | |
| ->all_select_cert_args() | |
| .empty()); | |
| return server_handshaker_->fake_proof_source_handle() | |
| ->all_select_cert_args() | |
| .back(); | |
| } | |
| // Should only be called when using FakeProofSourceHandle. | |
| FakeProofSourceHandle::ComputeSignatureArgs last_compute_signature_args() | |
| const { | |
| QUICHE_CHECK(server_handshaker_ && | |
| server_handshaker_->fake_proof_source_handle()); | |
| QUICHE_CHECK(!server_handshaker_->fake_proof_source_handle() | |
| ->all_compute_signature_args() | |
| .empty()); | |
| return server_handshaker_->fake_proof_source_handle() | |
| ->all_compute_signature_args() | |
| .back(); | |
| } | |
| protected: | |
| // Setup the client to send a (self-signed) client cert to the server, if | |
| // requested. InitializeFakeClient() must be called after this to take effect. | |
| bool SetupClientCert() { | |
| auto client_proof_source = std::make_unique<DefaultClientProofSource>(); | |
| CertificatePrivateKey client_cert_key( | |
| MakeKeyPairForSelfSignedCertificate()); | |
| CertificateOptions options; | |
| options.subject = "CN=subject"; | |
| options.serial_number = 0x12345678; | |
| options.validity_start = {2020, 1, 1, 0, 0, 0}; | |
| options.validity_end = {2049, 12, 31, 0, 0, 0}; | |
| std::string der_cert = | |
| CreateSelfSignedCertificate(*client_cert_key.private_key(), options); | |
| quiche::QuicheReferenceCountedPointer<ClientProofSource::Chain> | |
| client_cert_chain(new ClientProofSource::Chain({der_cert})); | |
| if (!client_proof_source->AddCertAndKey({"*"}, client_cert_chain, | |
| std::move(client_cert_key))) { | |
| return false; | |
| } | |
| client_crypto_config_->set_proof_source(std::move(client_proof_source)); | |
| return true; | |
| } | |
| // 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_; | |
| // Only set when initialized with InitializeServerWithFakeProofSourceHandle. | |
| NiceMock<TestTlsServerHandshaker>* server_handshaker_ = nullptr; | |
| TestTicketCrypter* ticket_crypter_; // owned by proof_source_ | |
| FakeProofSource* proof_source_; // owned by server_crypto_config_ | |
| std::unique_ptr<QuicCryptoServerConfig> server_crypto_config_; | |
| QuicCompressedCertsCache server_compressed_certs_cache_; | |
| QuicServerId server_id_; | |
| ClientCertMode initial_client_cert_mode_ = ClientCertMode::kNone; | |
| // Client state. | |
| PacketSavingConnection* client_connection_; | |
| std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_; | |
| std::unique_ptr<TestQuicSpdyClientSession> client_session_; | |
| crypto_test_utils::FakeClientOptions client_options_; | |
| // How many handshake messages have been moved from client to server and | |
| // server to client. | |
| std::pair<size_t, size_t> moved_messages_counts_ = {0, 0}; | |
| // Which QUIC versions the client and server support. | |
| ParsedQuicVersionVector supported_versions_; | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(TlsServerHandshakerTests, TlsServerHandshakerTest, | |
| ::testing::ValuesIn(GetTestParams()), | |
| ::testing::PrintToStringParamName()); | |
| TEST_P(TlsServerHandshakerTest, NotInitiallyConected) { | |
| EXPECT_FALSE(server_stream()->encryption_established()); | |
| EXPECT_FALSE(server_stream()->one_rtt_keys_available()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ConnectedAfterTlsHandshake) { | |
| CompleteCryptoHandshake(); | |
| EXPECT_TRUE(server_stream()->version().IsIetfQuic()); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_EQ(server_stream()->Sni(), kServerHostname); | |
| EXPECT_EQ(server_stream()->Alpn(), | |
| AlpnForVersion(server_stream()->version())); | |
| EXPECT_NE(server_stream()->Ciphersuite(), nullptr); | |
| } | |
| #if BORINGSSL_API_VERSION >= 41 | |
| TEST_P(TlsServerHandshakerTest, HandshakeWithServerPaddingNotEnabledByServer) { | |
| SetQuicRestartFlag(tls_server_padding_support, false); | |
| client_crypto_config_->ssl_config().server_padding_to_request = 128; | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(client_stream()->ServerPaddingSentForTesting()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, HandshakeWithServerPaddingEnabledByServer) { | |
| SetQuicRestartFlag(tls_server_padding_support, true); | |
| client_crypto_config_->ssl_config().server_padding_to_request = 128; | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(client_stream()->ServerPaddingSentForTesting()); | |
| } | |
| #endif | |
| TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSelectCertSuccess) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0); | |
| EXPECT_CALL(*server_connection_, CloseConnection(_, _, _)).Times(0); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| } | |
| TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSelectCertFailure) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::FAIL_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| // Check that the server didn't send any handshake messages, because it failed | |
| // to handshake. | |
| EXPECT_EQ(moved_messages_counts_.second, 0u); | |
| EXPECT_EQ(server_handshaker_->extra_error_details(), | |
| "select_cert_error: proof_source_handle async failure"); | |
| } | |
| TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSelectCertAndSignature) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_ASYNC); | |
| EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0); | |
| EXPECT_CALL(*server_connection_, CloseConnection(_, _, _)).Times(0); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| // A select cert operation is now pending. | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| EXPECT_EQ(server_handshaker_->expected_ssl_error(), | |
| SSL_ERROR_PENDING_CERTIFICATE); | |
| // Complete the pending select cert. It should advance the handshake to | |
| // compute a signature, which will also be saved as a pending operation. | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| // A compute signature operation is now pending. | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| EXPECT_EQ(server_handshaker_->expected_ssl_error(), | |
| SSL_ERROR_WANT_PRIVATE_KEY_OPERATION); | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| } | |
| TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSignature) { | |
| EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0); | |
| EXPECT_CALL(*server_connection_, CloseConnection(_, _, _)).Times(0); | |
| // Enable FakeProofSource to capture call to ComputeTlsSignature and run it | |
| // asynchronously. | |
| proof_source_->Activate(); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_EQ(proof_source_->NumPendingCallbacks(), 1); | |
| proof_source_->InvokePendingCallback(0); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| } | |
| TEST_P(TlsServerHandshakerTest, CancelPendingSelectCert) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0); | |
| EXPECT_CALL(*server_connection_, CloseConnection(_, _, _)).Times(0); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| server_handshaker_->CancelOutstandingCallbacks(); | |
| ASSERT_FALSE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| // CompletePendingOperation should be noop. | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| } | |
| TEST_P(TlsServerHandshakerTest, CancelPendingSignature) { | |
| EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0); | |
| EXPECT_CALL(*server_connection_, CloseConnection(_, _, _)).Times(0); | |
| // Enable FakeProofSource to capture call to ComputeTlsSignature and run it | |
| // asynchronously. | |
| proof_source_->Activate(); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_EQ(proof_source_->NumPendingCallbacks(), 1); | |
| server_session_ = nullptr; | |
| proof_source_->InvokePendingCallback(0); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ExtractSNI) { | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_EQ(server_stream()->crypto_negotiated_params().sni, | |
| "test.example.com"); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ServerConnectionIdPassedToSelectCert) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| // Disable early data. | |
| server_session_->set_early_data_enabled(false); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_EQ(last_select_cert_args().original_connection_id, TestConnectionId()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, HostnameForCertSelectionAndComputeSignature) { | |
| // Client uses upper case letters in hostname. It is considered valid by | |
| // QuicHostnameUtils::IsValidSNI, but it should be normalized for cert | |
| // selection. | |
| server_id_ = QuicServerId("tEsT.EXAMPLE.CoM", kServerPort); | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_EQ(server_stream()->crypto_negotiated_params().sni, | |
| "test.example.com"); | |
| EXPECT_EQ(last_select_cert_args().hostname, "test.example.com"); | |
| EXPECT_EQ(last_compute_signature_args().hostname, "test.example.com"); | |
| } | |
| TEST_P(TlsServerHandshakerTest, SSLConfigForCertSelection) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| // Disable early data. | |
| server_session_->set_early_data_enabled(false); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(last_select_cert_args().ssl_config.early_data_enabled); | |
| } | |
| TEST_P(TlsServerHandshakerTest, SelectCertificateCallsGetCertChains) { | |
| EXPECT_CALL(*proof_source_, GetCertChains(_, _, kServerHostname)); | |
| // Although the default implementation of `ProofSource::GetCertChains()` uses | |
| // `ProofSource::GetCertChain()`, we're using a `FakeProofSource` that | |
| // delegates to a `TestProofSource`, which does not have this behavior. | |
| EXPECT_CALL(*proof_source_, GetCertChain).Times(0); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ZeroCertChainsFailsHandshake) { | |
| EXPECT_CALL(*proof_source_, GetCertChains(_, _, kServerHostname)) | |
| .WillOnce(Return(ProofSource::CertChainsResult{ | |
| .chains_match_sni = false, | |
| .chains = {}, | |
| })); | |
| EXPECT_CALL(*proof_source_, GetCertChain).Times(0); | |
| AdvanceHandshakeWithFakeClient(); | |
| EXPECT_FALSE(client_stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(client_stream()->encryption_established()); | |
| EXPECT_FALSE(server_stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(server_stream()->encryption_established()); | |
| } | |
| // Test that `DefaultProofSourceHandle::SelectCertificate()` uses chains that do | |
| // not match the SNI when that's all that's available. | |
| TEST_P(TlsServerHandshakerTest, MultipleCertChainsNotMatchingSni) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| ASSERT_TRUE(server_handshaker_ != nullptr); | |
| // Note that if we called `server_handshaker_->SetupProofSourceHandle()` here, | |
| // this test would incorrectly exercise `FakeProofSourceHandle` instead of | |
| // `DefaultProofSourceHandle`. | |
| // Return chains that claim not to match SNI. (Whether the certificate in | |
| // `quic::test::kTestCertificate` actually matches `kServerHostname` is | |
| // irrelevant.) | |
| EXPECT_CALL(*proof_source_, GetCertChains(_, _, kServerHostname)) | |
| .WillOnce(Return(ProofSource::CertChainsResult{ | |
| .chains_match_sni = false, | |
| .chains = | |
| std::vector< | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain>>{ | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain>( | |
| new ProofSource::Chain( | |
| /*certs=*/{std::string(quic::test::kTestCertificate)}, | |
| /*trust_anchor_id=*/"")), | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain>( | |
| new ProofSource::Chain( | |
| /*certs=*/{std::string(quic::test::kTestCertificate)}, | |
| /*trust_anchor_id=*/"")), | |
| }, | |
| })); | |
| EXPECT_CALL(*proof_source_, GetCertChain).Times(0); | |
| EXPECT_CALL(*server_handshaker_, OnSelectCertificateDone( | |
| /*ok=*/true, /*is_sync=*/_, | |
| /*ssl_config=*/_, | |
| /*ticket_encryption_key=*/_, | |
| /*cert_matched_sni=*/false)); | |
| CompleteCryptoHandshake(); | |
| } | |
| // Test that `DefaultProofSourceHandle::SelectCertificate()` passes along chains | |
| // with and without Trust Anchor IDs . | |
| TEST_P(TlsServerHandshakerTest, SelectCertificateChainMatchesSni) { | |
| // Set the client-advertised Trust Anchor IDs. Each ID is preceded by an 8-bit | |
| // length prefix. | |
| client_crypto_config_->ssl_config().trust_anchor_ids = "\x03\x11\x22\x33"; | |
| InitializeFakeClient(); | |
| InitializeServerWithFakeProofSourceHandle(); | |
| ASSERT_TRUE(server_handshaker_ != nullptr); | |
| // Note that if we called `server_handshaker_->SetupProofSourceHandle()` here, | |
| // this test would incorrectly exercise `FakeProofSourceHandle` instead of | |
| // `DefaultProofSourceHandle`. | |
| using LocalSSLConfig = ProofSourceHandleCallback::LocalSSLConfig; | |
| std::vector<quiche::QuicheReferenceCountedPointer<ProofSource::Chain>> | |
| chains = { | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain>( | |
| new ProofSource::Chain( | |
| /*certs=*/{std::string(quic::test::kTestCertificate)}, | |
| /*trust_anchor_id=*/"\x11\x22\x33")), | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain>( | |
| new ProofSource::Chain( | |
| /*certs=*/{std::string(quic::test::kTestCertificate)}, | |
| /*trust_anchor_id=*/"")), | |
| }; | |
| EXPECT_CALL(*proof_source_, GetCertChains(_, _, kServerHostname)) | |
| .WillOnce(Return(ProofSource::CertChainsResult{ | |
| .chains_match_sni = true, | |
| .chains = chains, | |
| })); | |
| EXPECT_CALL(*proof_source_, GetCertChain).Times(0); | |
| // `DefaultProofSourceHandle::SelectCertificate()` should pick only the | |
| // chains that claim to match SNI. | |
| EXPECT_CALL( | |
| *server_handshaker_, | |
| OnSelectCertificateDone( | |
| /*ok=*/true, /*is_sync=*/_, | |
| VariantWith<LocalSSLConfig>(AllOf( | |
| Field(&LocalSSLConfig::chain, IsNull()), | |
| Field(&LocalSSLConfig::chains, ElementsAre(chains[0], chains[1])), | |
| Field(&LocalSSLConfig::delayed_ssl_config, | |
| Eq(QuicDelayedSSLConfig())))), | |
| /*ticket_encryption_key=*/_, | |
| /*cert_matched_sni=*/true)); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(client_stream()->MatchedTrustAnchorIdForTesting()); | |
| } | |
| // Test that `DefaultProofSourceHandle::SelectCertificate()` passes through | |
| // chains with Trust Anchor IDs when that's all that's available. The handshake | |
| // will succeed because one of the chains matches the client-advertised Trust | |
| // Anchor IDs. | |
| TEST_P(TlsServerHandshakerTest, | |
| SelectCertificateNotFirstChainMatchesSniAndTrustAnchorId) { | |
| // Set the client-advertised Trust Anchor IDs. Each ID is preceded by an 8-bit | |
| // length prefix. | |
| client_crypto_config_->ssl_config().trust_anchor_ids = "\x03\x11\x22\x33"; | |
| InitializeFakeClient(); | |
| InitializeServerWithFakeProofSourceHandle(); | |
| ASSERT_TRUE(server_handshaker_ != nullptr); | |
| // Note that if we called `server_handshaker_->SetupProofSourceHandle()` here, | |
| // this test would incorrectly exercise `FakeProofSourceHandle` instead of | |
| // `DefaultProofSourceHandle`. | |
| using LocalSSLConfig = ProofSourceHandleCallback::LocalSSLConfig; | |
| std::vector<quiche::QuicheReferenceCountedPointer<ProofSource::Chain>> | |
| chains = { | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain>( | |
| new ProofSource::Chain( | |
| /*certs=*/{std::string(quic::test::kTestCertificate)}, | |
| /*trust_anchor_id=*/"\x07\x08")), | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain>( | |
| new ProofSource::Chain( | |
| /*certs=*/{std::string(quic::test::kTestCertificate)}, | |
| /*trust_anchor_id=*/"\x11\x22\x33")), | |
| }; | |
| EXPECT_CALL(*proof_source_, GetCertChains(_, _, kServerHostname)) | |
| .WillOnce(Return(ProofSource::CertChainsResult{ | |
| .chains_match_sni = true, | |
| .chains = chains, | |
| })); | |
| EXPECT_CALL(*proof_source_, GetCertChain).Times(0); | |
| // `DefaultProofSourceHandle::SelectCertificate()` should pick only the | |
| // chains that claim to match SNI. | |
| EXPECT_CALL( | |
| *server_handshaker_, | |
| OnSelectCertificateDone( | |
| /*ok=*/true, /*is_sync=*/_, | |
| VariantWith<LocalSSLConfig>(AllOf( | |
| Field(&LocalSSLConfig::chain, IsNull()), | |
| Field(&LocalSSLConfig::chains, ElementsAre(chains[0], chains[1])), | |
| Field(&LocalSSLConfig::delayed_ssl_config, | |
| Eq(QuicDelayedSSLConfig())))), | |
| /*ticket_encryption_key=*/_, | |
| /*cert_matched_sni=*/true)); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(client_stream()->MatchedTrustAnchorIdForTesting()); | |
| } | |
| // Test that the handshake fails when all chains returned by | |
| // `ProofSource::GetCertChains()` have Trust Anchor IDs, but none match the | |
| // client-advertised IDs. This pitfall can be avoided by returning at least one | |
| // (complete) certificate chain that is not associated with a Trust Anchor ID. | |
| TEST_P(TlsServerHandshakerTest, | |
| SelectCertificateFailsWhenAllChainsHaveMismatchedTrustAnchorIds) { | |
| // Set the client-advertised Trust Anchor IDs. Each ID is preceded by an 8-bit | |
| // length prefix. | |
| client_crypto_config_->ssl_config().trust_anchor_ids = "\x03\x11\x22\x33"; | |
| InitializeFakeClient(); | |
| InitializeServerWithFakeProofSourceHandle(); | |
| ASSERT_TRUE(server_handshaker_ != nullptr); | |
| // Note that if we called `server_handshaker_->SetupProofSourceHandle()` here, | |
| // this test would incorrectly exercise `FakeProofSourceHandle` instead of | |
| // `DefaultProofSourceHandle`. | |
| using LocalSSLConfig = ProofSourceHandleCallback::LocalSSLConfig; | |
| std::vector<quiche::QuicheReferenceCountedPointer<ProofSource::Chain>> | |
| chains = { | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain>( | |
| new ProofSource::Chain( | |
| /*certs=*/{std::string(quic::test::kTestCertificate)}, | |
| /*trust_anchor_id=*/"\x07\x08")), | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain>( | |
| new ProofSource::Chain( | |
| /*certs=*/{std::string(quic::test::kTestCertificate)}, | |
| /*trust_anchor_id=*/"\x42")), | |
| }; | |
| EXPECT_CALL(*proof_source_, GetCertChains(_, _, kServerHostname)) | |
| .WillOnce(Return(ProofSource::CertChainsResult{ | |
| .chains_match_sni = true, | |
| .chains = chains, | |
| })); | |
| EXPECT_CALL(*proof_source_, GetCertChain).Times(0); | |
| // `DefaultProofSourceHandle::SelectCertificate()` should pick only the | |
| // chains that claim to match SNI. | |
| EXPECT_CALL( | |
| *server_handshaker_, | |
| OnSelectCertificateDone( | |
| /*ok=*/true, /*is_sync=*/_, | |
| VariantWith<LocalSSLConfig>(AllOf( | |
| Field(&LocalSSLConfig::chain, IsNull()), | |
| Field(&LocalSSLConfig::chains, ElementsAre(chains[0], chains[1])), | |
| Field(&LocalSSLConfig::delayed_ssl_config, | |
| Eq(QuicDelayedSSLConfig())))), | |
| /*ticket_encryption_key=*/_, | |
| /*cert_matched_sni=*/true)); | |
| AdvanceHandshakeWithFakeClient(); | |
| EXPECT_FALSE(client_stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(client_stream()->encryption_established()); | |
| EXPECT_FALSE(server_stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(server_stream()->encryption_established()); | |
| EXPECT_FALSE(client_stream()->MatchedTrustAnchorIdForTesting()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ConnectionClosedOnTlsError) { | |
| EXPECT_CALL(*server_connection_, | |
| CloseConnection(QUIC_HANDSHAKE_FAILED, _, _, _)); | |
| // Send a zero-length ClientHello from client to server. | |
| char bogus_handshake_message[] = { | |
| // Handshake struct (RFC 8446 appendix B.3) | |
| 1, // HandshakeType client_hello | |
| 0, | |
| 0, | |
| 0, // uint24 length | |
| }; | |
| // Install a packet flusher such that the packets generated by | |
| // |server_connection_| in response to this handshake message are more likely | |
| // to be coalesced and/or batched in the writer. | |
| // | |
| // This is required by TlsServerHandshaker because without the flusher, it | |
| // tends to generate many small, uncoalesced packets, one per | |
| // TlsHandshaker::WriteMessage. | |
| QuicConnection::ScopedPacketFlusher flusher(server_connection_); | |
| server_stream()->crypto_message_parser()->ProcessInput( | |
| absl::string_view(bogus_handshake_message, | |
| std::size(bogus_handshake_message)), | |
| ENCRYPTION_INITIAL); | |
| EXPECT_FALSE(server_stream()->one_rtt_keys_available()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ClientSendingBadALPN) { | |
| const std::string kTestBadClientAlpn = "bad-client-alpn"; | |
| EXPECT_CALL(*client_session_, GetAlpnsToOffer()) | |
| .WillOnce(Return(std::vector<std::string>({kTestBadClientAlpn}))); | |
| EXPECT_CALL( | |
| *server_connection_, | |
| CloseConnection( | |
| QUIC_HANDSHAKE_FAILED, | |
| static_cast<QuicIetfTransportErrorCodes>(CRYPTO_ERROR_FIRST + 120), | |
| HasSubstr("TLS handshake failure (ENCRYPTION_INITIAL) 120: " | |
| "no application protocol"), | |
| _)); | |
| AdvanceHandshakeWithFakeClient(); | |
| EXPECT_FALSE(client_stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(client_stream()->encryption_established()); | |
| EXPECT_FALSE(server_stream()->one_rtt_keys_available()); | |
| EXPECT_FALSE(server_stream()->encryption_established()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, CustomALPNNegotiation) { | |
| EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0); | |
| EXPECT_CALL(*server_connection_, CloseConnection(_, _, _)).Times(0); | |
| const std::string kTestAlpn = "A Custom ALPN Value"; | |
| const std::vector<std::string> kTestAlpns( | |
| {"foo", "bar", kTestAlpn, "something else"}); | |
| EXPECT_CALL(*client_session_, GetAlpnsToOffer()) | |
| .WillRepeatedly(Return(kTestAlpns)); | |
| EXPECT_CALL(*server_session_, SelectAlpn(_)) | |
| .WillOnce( | |
| [kTestAlpn, kTestAlpns](const std::vector<absl::string_view>& alpns) { | |
| EXPECT_THAT(alpns, testing::ElementsAreArray(kTestAlpns)); | |
| return std::find(alpns.cbegin(), alpns.cend(), kTestAlpn); | |
| }); | |
| EXPECT_CALL(*client_session_, OnAlpnSelected(absl::string_view(kTestAlpn))); | |
| EXPECT_CALL(*server_session_, OnAlpnSelected(absl::string_view(kTestAlpn))); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| } | |
| TEST_P(TlsServerHandshakerTest, RejectInvalidSNI) { | |
| SetQuicFlag(quic_client_allow_invalid_sni_for_test, true); | |
| server_id_ = QuicServerId("invalid!.example.com", kServerPort); | |
| InitializeFakeClient(); | |
| // Run the handshake and expect it to fail. | |
| AdvanceHandshakeWithFakeClient(); | |
| EXPECT_FALSE(server_stream()->encryption_established()); | |
| EXPECT_FALSE(server_stream()->one_rtt_keys_available()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, Resumption) { | |
| // Do the first handshake | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(client_stream()->IsResumption()); | |
| EXPECT_FALSE(server_stream()->IsResumption()); | |
| EXPECT_FALSE(server_stream()->ResumptionAttempted()); | |
| // Now do another handshake | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_NE(client_stream()->IsResumption(), GetParam().disable_resumption); | |
| EXPECT_NE(server_stream()->IsResumption(), GetParam().disable_resumption); | |
| EXPECT_NE(server_stream()->ResumptionAttempted(), | |
| GetParam().disable_resumption); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ResumptionWithAsyncDecryptCallback) { | |
| // Do the first handshake | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| ticket_crypter_->SetRunCallbacksAsync(true); | |
| // Now do another handshake | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| AdvanceHandshakeWithFakeClient(); | |
| if (GetParam().disable_resumption) { | |
| ASSERT_EQ(ticket_crypter_->NumPendingCallbacks(), 0u); | |
| return; | |
| } | |
| // Test that the DecryptCallback will be run asynchronously, and then run it. | |
| ASSERT_EQ(ticket_crypter_->NumPendingCallbacks(), 1u); | |
| ticket_crypter_->RunPendingCallback(0); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(client_stream()->IsResumption()); | |
| EXPECT_TRUE(server_stream()->IsResumption()); | |
| EXPECT_TRUE(server_stream()->ResumptionAttempted()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ResumptionWithPlaceholderTicket) { | |
| // Do the first handshake | |
| InitializeFakeClient(); | |
| ticket_crypter_->set_fail_encrypt(true); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(client_stream()->IsResumption()); | |
| EXPECT_FALSE(server_stream()->IsResumption()); | |
| EXPECT_FALSE(server_stream()->ResumptionAttempted()); | |
| // Now do another handshake. It should end up with a full handshake. When the | |
| // placeholder ticket is enabled, it will be undecryptable. When it is | |
| // disabled, newer BoringSSL servers will skip sending a ticket altogether, so | |
| // the client will not even attempt resumption. | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(client_stream()->IsResumption()); | |
| EXPECT_FALSE(server_stream()->IsResumption()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, AdvanceHandshakeDuringAsyncDecryptCallback) { | |
| if (GetParam().disable_resumption) { | |
| return; | |
| } | |
| // Do the first handshake | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| ticket_crypter_->SetRunCallbacksAsync(true); | |
| // Now do another handshake | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| InitializeFakeClient(); | |
| AdvanceHandshakeWithFakeClient(); | |
| // Ensure an async DecryptCallback is now pending. | |
| ASSERT_EQ(ticket_crypter_->NumPendingCallbacks(), 1u); | |
| { | |
| QuicConnection::ScopedPacketFlusher flusher(server_connection_); | |
| server_handshaker_->AdvanceHandshake(); | |
| } | |
| // This will delete |server_handshaker_|. | |
| server_session_ = nullptr; | |
| ticket_crypter_->RunPendingCallback(0); // Should not crash. | |
| } | |
| TEST_P(TlsServerHandshakerTest, ResumptionWithFailingDecryptCallback) { | |
| if (GetParam().disable_resumption) { | |
| return; | |
| } | |
| // Do the first handshake | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| ticket_crypter_->set_fail_decrypt(true); | |
| // Now do another handshake | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(client_stream()->IsResumption()); | |
| EXPECT_FALSE(server_stream()->IsResumption()); | |
| EXPECT_TRUE(server_stream()->ResumptionAttempted()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ResumptionWithFailingAsyncDecryptCallback) { | |
| if (GetParam().disable_resumption) { | |
| return; | |
| } | |
| // Do the first handshake | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| ticket_crypter_->set_fail_decrypt(true); | |
| ticket_crypter_->SetRunCallbacksAsync(true); | |
| // Now do another handshake | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| AdvanceHandshakeWithFakeClient(); | |
| // Test that the DecryptCallback will be run asynchronously, and then run it. | |
| ASSERT_EQ(ticket_crypter_->NumPendingCallbacks(), 1u); | |
| ticket_crypter_->RunPendingCallback(0); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(client_stream()->IsResumption()); | |
| EXPECT_FALSE(server_stream()->IsResumption()); | |
| EXPECT_TRUE(server_stream()->ResumptionAttempted()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, HandshakeFailsWithFailingProofSource) { | |
| InitializeServerConfigWithFailingProofSource(); | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| // Attempt handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| // Check that the server didn't send any handshake messages, because it failed | |
| // to handshake. | |
| EXPECT_EQ(moved_messages_counts_.second, 0u); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ZeroRttResumption) { | |
| std::vector<uint8_t> application_state = {0, 1, 2, 3}; | |
| // Do the first handshake | |
| server_stream()->SetServerApplicationStateForResumption( | |
| std::make_unique<ApplicationState>(application_state)); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(client_stream()->IsResumption()); | |
| EXPECT_FALSE(server_stream()->IsZeroRtt()); | |
| // Now do another handshake | |
| InitializeServer(); | |
| server_stream()->SetServerApplicationStateForResumption( | |
| std::make_unique<ApplicationState>(application_state)); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_NE(client_stream()->IsResumption(), GetParam().disable_resumption); | |
| EXPECT_NE(server_stream()->IsZeroRtt(), GetParam().disable_resumption); | |
| } | |
| TEST_P(TlsServerHandshakerTest, ZeroRttRejectOnApplicationStateChange) { | |
| std::vector<uint8_t> original_application_state = {1, 2}; | |
| std::vector<uint8_t> new_application_state = {3, 4}; | |
| // Do the first handshake | |
| server_stream()->SetServerApplicationStateForResumption( | |
| std::make_unique<ApplicationState>(original_application_state)); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(client_stream()->IsResumption()); | |
| EXPECT_FALSE(server_stream()->IsZeroRtt()); | |
| // Do another handshake, but change the application state | |
| InitializeServer(); | |
| server_stream()->SetServerApplicationStateForResumption( | |
| std::make_unique<ApplicationState>(new_application_state)); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_NE(client_stream()->IsResumption(), GetParam().disable_resumption); | |
| EXPECT_FALSE(server_stream()->IsZeroRtt()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, RequestClientCert) { | |
| ASSERT_TRUE(SetupClientCert()); | |
| InitializeFakeClient(); | |
| initial_client_cert_mode_ = ClientCertMode::kRequest; | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_handshaker_->received_client_cert()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, RequestClientCertAndVerify) { | |
| ASSERT_TRUE(SetupClientCert()); | |
| InitializeFakeClient(); | |
| initial_client_cert_mode_ = ClientCertMode::kRequest; | |
| auto proof_source = InitializeProofSource(); | |
| auto proof_verifier = std::make_unique<NiceMock<MockProofVerifier>>(); | |
| EXPECT_CALL(*proof_verifier, VerifyCertChain).WillOnce(Return(QUIC_SUCCESS)); | |
| InitializeServerConfigWithProofSourceAndVerifier(std::move(proof_source), | |
| std::move(proof_verifier)); | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_handshaker_->received_client_cert()); | |
| EXPECT_EQ(QuicAsyncStatus::QUIC_SUCCESS, | |
| server_handshaker_->verify_cert_chain_status()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, RequestClientCertAndFailVerification) { | |
| ASSERT_TRUE(SetupClientCert()); | |
| InitializeFakeClient(); | |
| initial_client_cert_mode_ = ClientCertMode::kRequest; | |
| auto proof_source = InitializeProofSource(); | |
| auto proof_verifier = std::make_unique<NiceMock<MockProofVerifier>>(); | |
| EXPECT_CALL(*proof_verifier, VerifyCertChain).WillOnce(Return(QUIC_FAILURE)); | |
| InitializeServerConfigWithProofSourceAndVerifier(std::move(proof_source), | |
| std::move(proof_verifier)); | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| // Advance handshake until it can't make any more progress due to the | |
| // failing proof verifier. | |
| while (!client_stream()->one_rtt_keys_available() || | |
| !server_stream()->one_rtt_keys_available()) { | |
| auto previous_moved_messages_counts = moved_messages_counts_; | |
| AdvanceHandshakeWithFakeClient(); | |
| // Break if the handshake has stopped making progress. | |
| if (previous_moved_messages_counts == moved_messages_counts_) { | |
| break; | |
| } | |
| } | |
| // Check that handshake did not finish successfully from the server side. | |
| EXPECT_FALSE(server_stream()->one_rtt_keys_available()); | |
| // Handshake is not complete and not confirmed. | |
| EXPECT_EQ(HANDSHAKE_PROCESSED, server_stream()->GetHandshakeState()); | |
| EXPECT_EQ(QuicAsyncStatus::QUIC_FAILURE, | |
| server_handshaker_->verify_cert_chain_status()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, | |
| SetInvalidServerTransportParamsByDelayedSslConfig) { | |
| ASSERT_TRUE(SetupClientCert()); | |
| InitializeFakeClient(); | |
| QuicDelayedSSLConfig delayed_ssl_config; | |
| delayed_ssl_config.quic_transport_parameters = {1, 2, 3}; | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| delayed_ssl_config); | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(server_handshaker_->fake_proof_source_handle() | |
| ->all_compute_signature_args() | |
| .empty()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, | |
| SetValidServerTransportParamsByDelayedSslConfig) { | |
| ParsedQuicVersion version = GetParam().version; | |
| TransportParameters server_params; | |
| std::string error_details; | |
| server_params.perspective = quic::Perspective::IS_SERVER; | |
| server_params.version_information = TransportParameters::VersionInformation(); | |
| server_params.version_information.value().chosen_version = | |
| quic::CreateQuicVersionLabel(version); | |
| server_params.version_information.value().other_versions = | |
| quic::CreateQuicVersionLabelVector( | |
| quic::ParsedQuicVersionVector{version}); | |
| ASSERT_TRUE(server_params.AreValid(&error_details)) << error_details; | |
| std::vector<uint8_t> server_params_bytes; | |
| ASSERT_TRUE( | |
| SerializeTransportParameters(server_params, &server_params_bytes)); | |
| ASSERT_TRUE(SetupClientCert()); | |
| InitializeFakeClient(); | |
| QuicDelayedSSLConfig delayed_ssl_config; | |
| delayed_ssl_config.quic_transport_parameters = server_params_bytes; | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| delayed_ssl_config); | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(server_handshaker_->fake_proof_source_handle() | |
| ->all_compute_signature_args() | |
| .empty()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, RequestClientCertByDelayedSslConfig) { | |
| ASSERT_TRUE(SetupClientCert()); | |
| InitializeFakeClient(); | |
| QuicDelayedSSLConfig delayed_ssl_config; | |
| delayed_ssl_config.client_cert_mode = ClientCertMode::kRequest; | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| delayed_ssl_config); | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_handshaker_->received_client_cert()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, RequestClientCert_NoCert) { | |
| initial_client_cert_mode_ = ClientCertMode::kRequest; | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_FALSE(server_handshaker_->received_client_cert()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, RequestAndRequireClientCert) { | |
| ASSERT_TRUE(SetupClientCert()); | |
| InitializeFakeClient(); | |
| initial_client_cert_mode_ = ClientCertMode::kRequire; | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_handshaker_->received_client_cert()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, RequestAndRequireClientCertByDelayedSslConfig) { | |
| ASSERT_TRUE(SetupClientCert()); | |
| InitializeFakeClient(); | |
| QuicDelayedSSLConfig delayed_ssl_config; | |
| delayed_ssl_config.client_cert_mode = ClientCertMode::kRequire; | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| delayed_ssl_config); | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_handshaker_->received_client_cert()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, RequestAndRequireClientCert_NoCert) { | |
| initial_client_cert_mode_ = ClientCertMode::kRequire; | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| EXPECT_CALL(*server_connection_, | |
| CloseConnection(QUIC_TLS_CERTIFICATE_REQUIRED, _, _, _)); | |
| AdvanceHandshakeWithFakeClient(); | |
| AdvanceHandshakeWithFakeClient(); | |
| EXPECT_FALSE(server_handshaker_->received_client_cert()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, CloseConnectionBeforeSelectCert) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action:: | |
| FAIL_SYNC_DO_NOT_CHECK_CLOSED, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| FAIL_SYNC_DO_NOT_CHECK_CLOSED); | |
| EXPECT_CALL(*server_handshaker_, OverrideQuicConfigDefaults(_)) | |
| .WillOnce([](QuicConfig* config) { | |
| QuicConfigPeer::SetReceivedMaxUnidirectionalStreams(config, | |
| /*max_streams=*/0); | |
| }); | |
| EXPECT_CALL(*server_connection_, | |
| CloseConnection(QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED, _, _)) | |
| .WillOnce([this](QuicErrorCode error, const std::string& details, | |
| ConnectionCloseBehavior connection_close_behavior) { | |
| server_connection_->ReallyCloseConnection(error, details, | |
| connection_close_behavior); | |
| ASSERT_FALSE(server_connection_->connected()); | |
| }); | |
| AdvanceHandshakeWithFakeClient(); | |
| EXPECT_TRUE(server_handshaker_->fake_proof_source_handle() | |
| ->all_select_cert_args() | |
| .empty()); | |
| } | |
| TEST_P(TlsServerHandshakerTest, FailUponCustomTranportParam) { | |
| client_session_->config()->custom_transport_parameters_to_send().emplace( | |
| TestTlsServerHandshaker::kFailHandshakeParam, | |
| "Fail handshake upon seeing this."); | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| EXPECT_CALL( | |
| *server_connection_, | |
| CloseConnection(QUIC_HANDSHAKE_FAILED, | |
| "Failed to process additional transport parameters", _)); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| } | |
| TEST_P(TlsServerHandshakerTest, SuccessWithCustomTranportParam) { | |
| client_session_->config()->custom_transport_parameters_to_send().emplace( | |
| TransportParameters::TransportParameterId{0xFFEADD}, | |
| "Continue upon seeing this."); | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| EXPECT_CALL(*server_connection_, CloseConnection(_, _, _)).Times(0); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| } | |
| TEST_P(TlsServerHandshakerTest, EnableMLKEM) { | |
| server_crypto_config_->set_preferred_groups({SSL_GROUP_X25519_MLKEM768}); | |
| client_crypto_config_->set_preferred_groups( | |
| {SSL_GROUP_X25519_MLKEM768, SSL_GROUP_X25519, SSL_GROUP_SECP256R1, | |
| SSL_GROUP_SECP384R1}); | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_stream()->version().IsIetfQuic()); | |
| EXPECT_EQ(SSL_GROUP_X25519_MLKEM768, | |
| SSL_get_group_id(server_stream()->GetSsl())); | |
| } | |
| TEST_P(TlsServerHandshakerTest, SetPreferredGroups) { | |
| // The server has a strictly ordered list of preferred groups. The negotiated | |
| // group is the first one in the list that is supported by the client. | |
| server_crypto_config_->set_preferred_groups( | |
| {SSL_GROUP_X25519_MLKEM768, SSL_GROUP_MLKEM1024, SSL_GROUP_X25519}); | |
| EXPECT_THAT(server_crypto_config_->preferred_groups(), | |
| ElementsAre(SSL_GROUP_X25519_MLKEM768, SSL_GROUP_MLKEM1024, | |
| SSL_GROUP_X25519)); | |
| EXPECT_THAT(server_crypto_config_->preferred_groups_flags(), | |
| ElementsAre(0, 0, 0)); | |
| EXPECT_FALSE(server_crypto_config_->preferred_groups_has_equipreference()); | |
| client_crypto_config_->set_preferred_groups( | |
| {SSL_GROUP_X25519, SSL_GROUP_MLKEM1024, SSL_GROUP_X25519_MLKEM768}); | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_stream()->version().IsIetfQuic()); | |
| EXPECT_EQ(SSL_GROUP_X25519_MLKEM768, | |
| SSL_get_group_id(server_stream()->GetSsl())); | |
| } | |
| TEST_P(TlsServerHandshakerTest, AppendEquallyPreferredGroupsStrictlyOrdered) { | |
| // The server's preferred groups are equivalent to the test case above, but | |
| // configured using append_equally_preferred_groups() with single-element | |
| // equipreference groups. | |
| server_crypto_config_->append_equally_preferred_groups( | |
| {SSL_GROUP_X25519_MLKEM768}); | |
| server_crypto_config_->append_equally_preferred_groups({SSL_GROUP_MLKEM1024}); | |
| server_crypto_config_->append_equally_preferred_groups({SSL_GROUP_X25519}); | |
| EXPECT_THAT(server_crypto_config_->preferred_groups(), | |
| ElementsAre(SSL_GROUP_X25519_MLKEM768, SSL_GROUP_MLKEM1024, | |
| SSL_GROUP_X25519)); | |
| EXPECT_THAT(server_crypto_config_->preferred_groups_flags(), | |
| ElementsAre(0, 0, 0)); | |
| // This is true because append_equally_preferred_groups() was called, even | |
| // though all groups in the list are strictly ordered. | |
| EXPECT_TRUE(server_crypto_config_->preferred_groups_has_equipreference()); | |
| client_crypto_config_->set_preferred_groups( | |
| {SSL_GROUP_X25519, SSL_GROUP_MLKEM1024, SSL_GROUP_X25519_MLKEM768}); | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_stream()->version().IsIetfQuic()); | |
| EXPECT_EQ(SSL_GROUP_X25519_MLKEM768, | |
| SSL_get_group_id(server_stream()->GetSsl())); | |
| } | |
| TEST_P(TlsServerHandshakerTest, AppendEquallyPreferredGroupsEquipreference) { | |
| // The server has two equally preferred groups. Between them, the client's | |
| // preference determines the one chosen. | |
| server_crypto_config_->append_equally_preferred_groups( | |
| {SSL_GROUP_X25519_MLKEM768, SSL_GROUP_MLKEM1024}); | |
| server_crypto_config_->append_equally_preferred_groups({SSL_GROUP_X25519}); | |
| EXPECT_THAT(server_crypto_config_->preferred_groups(), | |
| ElementsAre(SSL_GROUP_X25519_MLKEM768, SSL_GROUP_MLKEM1024, | |
| SSL_GROUP_X25519)); | |
| EXPECT_THAT(server_crypto_config_->preferred_groups_flags(), | |
| ElementsAre(SSL_GROUP_FLAG_EQUAL_PREFERENCE_WITH_NEXT, 0, 0)); | |
| EXPECT_TRUE(server_crypto_config_->preferred_groups_has_equipreference()); | |
| struct { | |
| std::vector<uint16_t> client_preferred_groups; | |
| uint16_t expected_negotiated_group; | |
| } kTestCases[] = { | |
| {{SSL_GROUP_X25519, SSL_GROUP_MLKEM1024, SSL_GROUP_X25519_MLKEM768}, | |
| SSL_GROUP_MLKEM1024}, | |
| {{SSL_GROUP_X25519, SSL_GROUP_X25519_MLKEM768, SSL_GROUP_MLKEM1024}, | |
| SSL_GROUP_X25519_MLKEM768}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| client_crypto_config_->set_preferred_groups(test.client_preferred_groups); | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_stream()->version().IsIetfQuic()); | |
| EXPECT_EQ(test.expected_negotiated_group, | |
| SSL_get_group_id(server_stream()->GetSsl())); | |
| } | |
| } | |
| TEST_P(TlsServerHandshakerTest, SetPreferredGroupsFollowedByEquipreference) { | |
| // Test that mixing the two config setters works as expected. | |
| server_crypto_config_->set_preferred_groups( | |
| {SSL_GROUP_X25519_MLKEM768, SSL_GROUP_X25519}); | |
| server_crypto_config_->append_equally_preferred_groups( | |
| {SSL_GROUP_SECP256R1, SSL_GROUP_SECP384R1}); | |
| EXPECT_THAT(server_crypto_config_->preferred_groups(), | |
| ElementsAre(SSL_GROUP_X25519_MLKEM768, SSL_GROUP_X25519, | |
| SSL_GROUP_SECP256R1, SSL_GROUP_SECP384R1)); | |
| EXPECT_THAT(server_crypto_config_->preferred_groups_flags(), | |
| ElementsAre(0, 0, SSL_GROUP_FLAG_EQUAL_PREFERENCE_WITH_NEXT, 0)); | |
| EXPECT_TRUE(server_crypto_config_->preferred_groups_has_equipreference()); | |
| struct { | |
| std::vector<uint16_t> client_preferred_groups; | |
| uint16_t expected_negotiated_group; | |
| } kTestCases[] = { | |
| {{SSL_GROUP_SECP384R1, SSL_GROUP_SECP256R1, SSL_GROUP_X25519_MLKEM768, | |
| SSL_GROUP_X25519}, | |
| SSL_GROUP_X25519_MLKEM768}, | |
| {{SSL_GROUP_SECP384R1, SSL_GROUP_SECP256R1, SSL_GROUP_X25519}, | |
| SSL_GROUP_X25519}, | |
| {{SSL_GROUP_SECP384R1, SSL_GROUP_SECP256R1}, SSL_GROUP_SECP384R1}, | |
| {{SSL_GROUP_SECP256R1, SSL_GROUP_SECP384R1}, SSL_GROUP_SECP256R1}, | |
| }; | |
| for (const auto& test : kTestCases) { | |
| client_crypto_config_->set_preferred_groups(test.client_preferred_groups); | |
| InitializeServer(); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_stream()->version().IsIetfQuic()); | |
| EXPECT_EQ(test.expected_negotiated_group, | |
| SSL_get_group_id(server_stream()->GetSsl())); | |
| } | |
| } | |
| TEST_P(TlsServerHandshakerTest, AlpsUseNewCodepoint) { | |
| const struct { | |
| bool client_use_alps_new_codepoint; | |
| } tests[] = { | |
| // The intent of this test is to demonstrate different combinations of | |
| // ALPS codepoint settings works well. | |
| {true}, | |
| {false}, | |
| }; | |
| for (size_t i = 0; i < std::size(tests); i++) { | |
| SCOPED_TRACE(absl::StrCat("Test #", i)); | |
| const auto& test = tests[i]; | |
| client_crypto_config_->set_alps_use_new_codepoint( | |
| test.client_use_alps_new_codepoint); | |
| ASSERT_TRUE(SetupClientCert()); | |
| InitializeFakeClient(); | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| EXPECT_EQ(test.client_use_alps_new_codepoint, | |
| server_handshaker_->UseAlpsNewCodepoint()); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_TRUE(server_stream()->version().IsIetfQuic()); | |
| } | |
| } | |
| TEST_P(TlsServerHandshakerTest, NoCompliancePolicy) { | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain( | |
| new ProofSource::Chain({std::string(quic::test::kTestCertificate)})); | |
| // Arrange GetCertChains to return no compliance policy. | |
| EXPECT_CALL(*proof_source_, GetCertChains(_, _, kServerHostname)) | |
| .WillOnce(Return(ProofSource::CertChainsResult{ | |
| .chains_match_sni = true, | |
| .chains = {chain}, | |
| .ssl_compliance_policy = std::nullopt, | |
| })); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_NE(SSL_CIPHER_get_id(SSL_get_current_cipher(server_ssl())), | |
| TLS1_3_CK_AES_256_GCM_SHA384); | |
| } | |
| TEST_P(TlsServerHandshakerTest, CompliancePolicyWithSynchronousSelectCert) { | |
| quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain( | |
| new ProofSource::Chain({std::string(quic::test::kTestCertificate)})); | |
| // Arrange GetCertChains to return the compliance policy. | |
| EXPECT_CALL(*proof_source_, GetCertChains(_, _, kServerHostname)) | |
| .WillOnce(Return(ProofSource::CertChainsResult{ | |
| .chains_match_sni = true, | |
| .chains = {chain}, | |
| .ssl_compliance_policy = ssl_compliance_policy_cnsa_202407, | |
| })); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| // See why AES_256 is preferred when using cnsa_202407 at: | |
| // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#Compliance-policy-configurations | |
| EXPECT_EQ(SSL_CIPHER_get_id(SSL_get_current_cipher(server_ssl())), | |
| TLS1_3_CK_AES_256_GCM_SHA384); | |
| } | |
| TEST_P(TlsServerHandshakerTest, CompliancePolicyWithAsynchronousSelectCert) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| // Configure the handshaker to perform async cert selection with the policy. | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| QuicDelayedSSLConfig{.ssl_compliance_policy = | |
| ssl_compliance_policy_cnsa_202407}); | |
| // Start handshake. | |
| AdvanceHandshakeWithFakeClient(); | |
| // Complete the async select_certificate operation. | |
| ASSERT_TRUE( | |
| server_handshaker_->fake_proof_source_handle()->HasPendingOperation()); | |
| server_handshaker_->fake_proof_source_handle()->CompletePendingOperation(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| // See why AES_256 is preferred when using cnsa_202407 at: | |
| // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#Compliance-policy-configurations | |
| EXPECT_EQ(SSL_CIPHER_get_id(SSL_get_current_cipher(server_ssl())), | |
| TLS1_3_CK_AES_256_GCM_SHA384); | |
| } | |
| TEST_P(TlsServerHandshakerTest, GetCredentialExData) { | |
| InitializeServerWithFakeProofSourceHandle(); | |
| server_handshaker_->EnableInfoCallback(); | |
| bool credential_ex_data_verified = false; | |
| EXPECT_CALL(*server_handshaker_, InfoCallback(_, _)) | |
| .WillRepeatedly([&](int type, int /*value*/) { | |
| if (type & SSL_CB_HANDSHAKE_DONE) { | |
| SSL* ssl = server_ssl(); | |
| // Get the selected credential used for this connection. | |
| const SSL_CREDENTIAL* cred = SSL_get0_selected_credential(ssl); | |
| ASSERT_NE(cred, nullptr); | |
| // Verify that the extension data is present and non-null. | |
| const CredentialExData* ex_data = GetCredentialExData(*cred); | |
| ASSERT_NE(ex_data, nullptr); | |
| credential_ex_data_verified = true; | |
| } | |
| }); | |
| server_handshaker_->SetupProofSourceHandle( | |
| /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, | |
| /*compute_signature_action=*/FakeProofSourceHandle::Action:: | |
| DELEGATE_SYNC); | |
| InitializeFakeClient(); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| ASSERT_TRUE(credential_ex_data_verified); | |
| } | |
| TEST_P(TlsServerHandshakerTest, CachedSslInfoAfterResetSsl) { | |
| SetQuicReloadableFlag(quic_reset_server_params_in_reset_ssl, true); | |
| CompleteCryptoHandshake(); | |
| ExpectHandshakeSuccessful(); | |
| EXPECT_EQ(server_stream()->Sni(), kServerHostname); | |
| EXPECT_EQ(server_stream()->Alpn(), | |
| AlpnForVersion(server_stream()->version())); | |
| const SSL_CIPHER* cipher = server_stream()->Ciphersuite(); | |
| uint16_t cipher_id = server_stream()->CiphersuiteId(); | |
| std::string cipher_string(server_stream()->CiphersuiteString()); | |
| uint16_t tls_group_id = server_stream()->TlsGroupId(); | |
| std::string tls_group_string(server_stream()->TlsGroupString()); | |
| EXPECT_NE(cipher, nullptr); | |
| EXPECT_NE(cipher_id, 0); | |
| EXPECT_NE(cipher_string, ""); | |
| EXPECT_NE(tls_group_id, 0); | |
| EXPECT_NE(tls_group_string, ""); | |
| // Call ResetSsl. This should cache the SSL info. | |
| TlsServerHandshaker* handshaker = | |
| static_cast<TlsServerHandshaker*>(server_stream()); | |
| EXPECT_NE(TlsServerHandshakerPeer::GetServerParams(handshaker), nullptr); | |
| handshaker->ResetSsl(); | |
| // Verify that the info is still available from the cache. | |
| EXPECT_EQ(server_stream()->Sni(), kServerHostname); | |
| EXPECT_EQ(server_stream()->Alpn(), | |
| AlpnForVersion(server_stream()->version())); | |
| EXPECT_EQ(server_stream()->Ciphersuite(), cipher); | |
| EXPECT_EQ(server_stream()->CiphersuiteId(), cipher_id); | |
| EXPECT_EQ(server_stream()->CiphersuiteString(), cipher_string); | |
| EXPECT_EQ(server_stream()->TlsGroupId(), tls_group_id); | |
| EXPECT_EQ(server_stream()->TlsGroupString(), tls_group_string); | |
| EXPECT_EQ(TlsServerHandshakerPeer::GetServerParams(handshaker), nullptr); | |
| } | |
| } // namespace | |
| } // namespace test | |
| } // namespace quic |