blob: 0053f759f0ca715cc11b2121eccf99873b720a85 [file] [log] [blame] [edit]
// 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 <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/macros.h"
#include "openssl/hpke.h"
#include "openssl/ssl.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::_;
using testing::HasSubstr;
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),
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(), ssl_config_);
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);
});
}
static bssl::UniquePtr<SSL_ECH_KEYS> MakeTestEchKeys(
const char* public_name, size_t max_name_len,
std::string* ech_config_list) {
bssl::ScopedEVP_HPKE_KEY key;
if (!EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256())) {
return nullptr;
}
uint8_t* ech_config;
size_t ech_config_len;
if (!SSL_marshal_ech_config(&ech_config, &ech_config_len,
/*config_id=*/1, key.get(), public_name,
max_name_len)) {
return nullptr;
}
bssl::UniquePtr<uint8_t> scoped_ech_config(ech_config);
uint8_t* ech_config_list_raw;
size_t ech_config_list_len;
bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
if (!keys ||
!SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1, ech_config,
ech_config_len, key.get()) ||
!SSL_ECH_KEYS_marshal_retry_configs(keys.get(), &ech_config_list_raw,
&ech_config_list_len)) {
return nullptr;
}
bssl::UniquePtr<uint8_t> scoped_ech_config_list(ech_config_list_raw);
ech_config_list->assign(ech_config_list_raw,
ech_config_list_raw + ech_config_list_len);
return keys;
}
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_;
std::optional<QuicSSLConfig> ssl_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()->ResumptionAttempted());
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()->ResumptionAttempted());
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()->ResumptionAttempted());
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_TRUE(stream()->ResumptionAttempted());
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),
HasSubstr("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()));
}
TEST_P(TlsClientHandshakerTest, ECH) {
ssl_config_.emplace();
bssl::UniquePtr<SSL_ECH_KEYS> ech_keys =
MakeTestEchKeys("public-name.example", /*max_name_len=*/64,
&ssl_config_->ech_config_list);
ASSERT_TRUE(ech_keys);
// Configure the server to use the test ECH keys.
ASSERT_TRUE(
SSL_CTX_set1_ech_keys(server_crypto_config_->ssl_ctx(), ech_keys.get()));
// Recreate the client to pick up the new `ssl_config_`.
CreateConnection();
// The handshake should complete and negotiate ECH.
CompleteCryptoHandshake();
EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
EXPECT_TRUE(stream()->encryption_established());
EXPECT_TRUE(stream()->one_rtt_keys_available());
EXPECT_TRUE(stream()->crypto_negotiated_params().encrypted_client_hello);
}
TEST_P(TlsClientHandshakerTest, ECHWithConfigAndGREASE) {
ssl_config_.emplace();
bssl::UniquePtr<SSL_ECH_KEYS> ech_keys =
MakeTestEchKeys("public-name.example", /*max_name_len=*/64,
&ssl_config_->ech_config_list);
ASSERT_TRUE(ech_keys);
ssl_config_->ech_grease_enabled = true;
// Configure the server to use the test ECH keys.
ASSERT_TRUE(
SSL_CTX_set1_ech_keys(server_crypto_config_->ssl_ctx(), ech_keys.get()));
// Recreate the client to pick up the new `ssl_config_`.
CreateConnection();
// When both ECH and ECH GREASE are enabled, ECH should take precedence.
// The handshake should complete and negotiate ECH.
CompleteCryptoHandshake();
EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
EXPECT_TRUE(stream()->encryption_established());
EXPECT_TRUE(stream()->one_rtt_keys_available());
EXPECT_TRUE(stream()->crypto_negotiated_params().encrypted_client_hello);
}
TEST_P(TlsClientHandshakerTest, ECHInvalidConfig) {
// An invalid ECHConfigList should fail before sending a ClientHello.
ssl_config_.emplace();
ssl_config_->ech_config_list = "invalid config";
CreateConnection();
EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _));
stream()->CryptoConnect();
}
TEST_P(TlsClientHandshakerTest, ECHWrongKeys) {
ssl_config_.emplace();
bssl::UniquePtr<SSL_ECH_KEYS> ech_keys1 =
MakeTestEchKeys("public-name.example", /*max_name_len=*/64,
&ssl_config_->ech_config_list);
ASSERT_TRUE(ech_keys1);
std::string ech_config_list2;
bssl::UniquePtr<SSL_ECH_KEYS> ech_keys2 = MakeTestEchKeys(
"public-name.example", /*max_name_len=*/64, &ech_config_list2);
ASSERT_TRUE(ech_keys2);
// Configure the server to use different keys from what the client has.
ASSERT_TRUE(
SSL_CTX_set1_ech_keys(server_crypto_config_->ssl_ctx(), ech_keys2.get()));
// Recreate the client to pick up the new `ssl_config_`.
CreateConnection();
// TODO(crbug.com/1287248): This should instead output sufficient information
// to run the recovery flow.
EXPECT_CALL(*connection_,
CloseConnection(QUIC_HANDSHAKE_FAILED,
static_cast<QuicIetfTransportErrorCodes>(
CRYPTO_ERROR_FIRST + SSL_AD_ECH_REQUIRED),
_, _))
.WillOnce(testing::Invoke(connection_,
&MockQuicConnection::ReallyCloseConnection4));
// The handshake should complete and negotiate ECH.
CompleteCryptoHandshake();
}
// Test that ECH GREASE can be configured.
TEST_P(TlsClientHandshakerTest, ECHGrease) {
ssl_config_.emplace();
ssl_config_->ech_grease_enabled = true;
CreateConnection();
// Add a DoS callback on the server, to test that the client sent a GREASE
// message. This is a bit of a hack. TlsServerHandshaker already configures
// the certificate selection callback, but does not usefully expose any way
// for tests to inspect the ClientHello. So, instead, we register a different
// callback that also gets the ClientHello.
static bool callback_ran;
callback_ran = false;
SSL_CTX_set_dos_protection_cb(
server_crypto_config_->ssl_ctx(),
[](const SSL_CLIENT_HELLO* client_hello) -> int {
const uint8_t* data;
size_t len;
EXPECT_TRUE(SSL_early_callback_ctx_extension_get(
client_hello, TLSEXT_TYPE_encrypted_client_hello, &data, &len));
callback_ran = true;
return 1;
});
CompleteCryptoHandshake();
EXPECT_TRUE(callback_ran);
EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
EXPECT_TRUE(stream()->encryption_established());
EXPECT_TRUE(stream()->one_rtt_keys_available());
// Sending an ignored ECH GREASE extension does not count as negotiating ECH.
EXPECT_FALSE(stream()->crypto_negotiated_params().encrypted_client_hello);
}
#if BORINGSSL_API_VERSION >= 22
TEST_P(TlsClientHandshakerTest, EnableKyber) {
crypto_config_->set_preferred_groups({SSL_GROUP_X25519_KYBER768_DRAFT00});
server_crypto_config_->set_preferred_groups(
{SSL_GROUP_X25519_KYBER768_DRAFT00, SSL_GROUP_X25519, SSL_GROUP_SECP256R1,
SSL_GROUP_SECP384R1});
CreateConnection();
CompleteCryptoHandshake();
EXPECT_TRUE(stream()->encryption_established());
EXPECT_TRUE(stream()->one_rtt_keys_available());
EXPECT_EQ(SSL_GROUP_X25519_KYBER768_DRAFT00,
SSL_get_group_id(stream()->GetSsl()));
}
#endif // BORINGSSL_API_VERSION
#if BORINGSSL_API_VERSION >= 27
TEST_P(TlsClientHandshakerTest, EnableClientAlpsUseNewCodepoint) {
// The intent of this test is to demonstrate no matter whether server
// allows the new ALPS codepoint or not, the handshake should complete
// successfully.
for (bool server_allow_alps_new_codepoint : {true, false}) {
SCOPED_TRACE(absl::StrCat("Test allows alps new codepoint:",
server_allow_alps_new_codepoint));
crypto_config_->set_alps_use_new_codepoint(true);
SetQuicReloadableFlag(quic_gfe_allow_alps_new_codepoint,
server_allow_alps_new_codepoint);
CreateConnection();
// Add a DoS callback on the server, to test that the client sent the new
// ALPS codepoint.
static bool callback_ran;
callback_ran = false;
SSL_CTX_set_dos_protection_cb(
server_crypto_config_->ssl_ctx(),
[](const SSL_CLIENT_HELLO* client_hello) -> int {
const uint8_t* data;
size_t len;
EXPECT_TRUE(SSL_early_callback_ctx_extension_get(
client_hello, TLSEXT_TYPE_application_settings, &data, &len));
callback_ran = true;
return 1;
});
CompleteCryptoHandshake();
EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
EXPECT_TRUE(callback_ran);
}
}
#endif // BORINGSSL_API_VERSION
} // namespace
} // namespace test
} // namespace quic