blob: 2a51402d6601d45ac740d28fbdfa6157439af707 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/quic/quic_crypto_server_stream.h"
#include <map>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
#include "net/quic/crypto/crypto_framer.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/crypto/crypto_utils.h"
#include "net/quic/crypto/quic_crypto_server_config.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_session.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/delayed_verify_strike_register_client.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
class QuicConnection;
class ReliableQuicStream;
} // namespace net
using std::pair;
using std::string;
using testing::_;
namespace net {
namespace test {
class QuicCryptoServerConfigPeer {
public:
static string GetPrimaryOrbit(const QuicCryptoServerConfig& config) {
base::AutoLock lock(config.configs_lock_);
CHECK(config.primary_config_.get() != nullptr);
return string(reinterpret_cast<const char*>(config.primary_config_->orbit),
kOrbitSize);
}
};
class QuicCryptoServerStreamPeer {
public:
static bool DoesPeerSupportStatelessRejects(
const CryptoHandshakeMessage& message) {
return net::QuicCryptoServerStream::DoesPeerSupportStatelessRejects(
message);
}
};
namespace {
const char kServerHostname[] = "test.example.com";
const uint16 kServerPort = 80;
class QuicCryptoServerStreamTest : public ::testing::TestWithParam<bool> {
public:
QuicCryptoServerStreamTest()
: server_crypto_config_(QuicCryptoServerConfig::TESTING,
QuicRandom::GetInstance()),
server_id_(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED) {
// TODO(wtc): replace this with ProofSourceForTesting() when Chromium has
// a working ProofSourceForTesting().
server_crypto_config_.SetProofSource(
CryptoTestUtils::FakeProofSourceForTesting());
server_crypto_config_.set_strike_register_no_startup_period();
InitializeServer();
if (AsyncStrikeRegisterVerification()) {
string orbit =
QuicCryptoServerConfigPeer::GetPrimaryOrbit(server_crypto_config_);
strike_register_client_ = new DelayedVerifyStrikeRegisterClient(
10000, // strike_register_max_entries
static_cast<uint32>(
server_connection_->clock()->WallNow().ToUNIXSeconds()),
60, // strike_register_window_secs
reinterpret_cast<const uint8*>(orbit.data()),
StrikeRegister::NO_STARTUP_PERIOD_NEEDED);
strike_register_client_->StartDelayingVerification();
server_crypto_config_.SetStrikeRegisterClient(strike_register_client_);
}
}
// Initializes the crypto server stream state for testing. May be
// called multiple times.
void InitializeServer() {
TestServerSession* server_session = nullptr;
QuicCryptoServerStream* server_stream = nullptr;
SetupCryptoServerStreamForTest(server_id_,
QuicTime::Delta::FromSeconds(100000),
&server_crypto_config_, &server_connection_,
&server_session, &server_stream);
CHECK(server_session);
CHECK(server_stream);
server_session_.reset(server_session);
server_stream_.reset(server_stream);
CryptoTestUtils::SetupCryptoServerConfigForTest(
server_connection_->clock(), server_connection_->random_generator(),
server_session_->config(), &server_crypto_config_);
}
// Initializes a fake client, and all its associated state, for
// testing. May be called multiple times.
void InitializeFakeClient(bool supports_stateless_rejects) {
TestClientSession* client_session = nullptr;
QuicCryptoClientStream* client_stream = nullptr;
SetupCryptoClientStreamForTest(server_id_, supports_stateless_rejects,
QuicTime::Delta::FromSeconds(100000),
&client_crypto_config_, &client_connection_,
&client_session, &client_stream);
CHECK(client_session);
CHECK(client_stream);
client_session_.reset(client_session);
client_stream_.reset(client_stream);
}
bool AsyncStrikeRegisterVerification() {
return GetParam();
}
void ConstructHandshakeMessage() {
CryptoFramer framer;
message_data_.reset(framer.ConstructHandshakeMessage(message_));
}
int CompleteCryptoHandshake() {
CHECK(server_connection_);
CHECK(server_stream_ != nullptr);
return CryptoTestUtils::HandshakeWithFakeClient(
server_connection_, server_stream_.get(), client_options_);
}
// Performs a single round of handshake message-exchange between the
// client and server.
void AdvanceHandshakeWithFakeClient() {
CHECK(server_connection_);
CHECK(server_stream_ != nullptr);
CHECK(client_session_ != nullptr);
CHECK(client_stream_ != nullptr);
EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber());
client_stream_->CryptoConnect();
CryptoTestUtils::AdvanceHandshake(client_connection_, client_stream_.get(),
0, server_connection_,
server_stream_.get(), 0);
}
protected:
// Server state
PacketSavingConnection* server_connection_;
scoped_ptr<TestServerSession> server_session_;
QuicCryptoServerConfig server_crypto_config_;
scoped_ptr<QuicCryptoServerStream> server_stream_;
QuicServerId server_id_;
// Client state
PacketSavingConnection* client_connection_;
QuicCryptoClientConfig client_crypto_config_;
scoped_ptr<TestClientSession> client_session_;
scoped_ptr<QuicCryptoClientStream> client_stream_;
CryptoHandshakeMessage message_;
scoped_ptr<QuicData> message_data_;
CryptoTestUtils::FakeClientOptions client_options_;
DelayedVerifyStrikeRegisterClient* strike_register_client_;
};
INSTANTIATE_TEST_CASE_P(Tests, QuicCryptoServerStreamTest, testing::Bool());
TEST_P(QuicCryptoServerStreamTest, NotInitiallyConected) {
EXPECT_FALSE(server_stream_->encryption_established());
EXPECT_FALSE(server_stream_->handshake_confirmed());
}
TEST_P(QuicCryptoServerStreamTest, NotInitiallySendingStatelessRejects) {
EXPECT_FALSE(server_stream_->use_stateless_rejects_if_peer_supported());
EXPECT_FALSE(server_stream_->peer_supports_stateless_rejects());
}
TEST_P(QuicCryptoServerStreamTest, ConnectedAfterCHLO) {
// CompleteCryptoHandshake returns the number of client hellos sent. This
// test should send:
// * One to get a source-address token and certificates.
// * One to complete the handshake.
EXPECT_EQ(2, CompleteCryptoHandshake());
EXPECT_TRUE(server_stream_->encryption_established());
EXPECT_TRUE(server_stream_->handshake_confirmed());
}
TEST_P(QuicCryptoServerStreamTest, StatelessRejectAfterCHLO) {
ValueRestore<bool> old_flag(&FLAGS_enable_quic_stateless_reject_support,
true);
server_stream_->set_use_stateless_rejects_if_peer_supported(true);
InitializeFakeClient(/* supports_stateless_rejects= */ true);
AdvanceHandshakeWithFakeClient();
// Check the server to make the sure the handshake did not succeed.
EXPECT_FALSE(server_stream_->encryption_established());
EXPECT_FALSE(server_stream_->handshake_confirmed());
// Check the client state to make sure that it received a server-designated
// connection id.
QuicCryptoClientConfig::CachedState* client_state =
client_crypto_config_.LookupOrCreate(server_id_);
ASSERT_TRUE(client_state->has_server_nonce());
ASSERT_FALSE(client_state->GetNextServerNonce().empty());
ASSERT_FALSE(client_state->has_server_nonce());
ASSERT_TRUE(client_state->has_server_designated_connection_id());
const QuicConnectionId server_designated_connection_id =
client_state->GetNextServerDesignatedConnectionId();
const QuicConnectionId expected_id =
reinterpret_cast<MockRandom*>(server_connection_->random_generator())
->RandUint64();
EXPECT_EQ(expected_id, server_designated_connection_id);
EXPECT_FALSE(client_state->has_server_designated_connection_id());
ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
}
TEST_P(QuicCryptoServerStreamTest, ConnectedAfterStatelessHandshake) {
ValueRestore<bool> old_flag(&FLAGS_enable_quic_stateless_reject_support,
true);
server_stream_->set_use_stateless_rejects_if_peer_supported(true);
InitializeFakeClient(/* supports_stateless_rejects= */ true);
AdvanceHandshakeWithFakeClient();
// On the first round, encryption will not be established.
EXPECT_FALSE(server_stream_->encryption_established());
EXPECT_FALSE(server_stream_->handshake_confirmed());
EXPECT_EQ(1, server_stream_->num_handshake_messages());
EXPECT_EQ(0, server_stream_->num_handshake_messages_with_server_nonces());
// Now check the client state.
QuicCryptoClientConfig::CachedState* client_state =
client_crypto_config_.LookupOrCreate(server_id_);
ASSERT_TRUE(client_state->has_server_designated_connection_id());
const QuicConnectionId server_designated_connection_id =
client_state->GetNextServerDesignatedConnectionId();
const QuicConnectionId expected_id =
reinterpret_cast<MockRandom*>(server_connection_->random_generator())
->RandUint64();
EXPECT_EQ(expected_id, server_designated_connection_id);
EXPECT_FALSE(client_state->has_server_designated_connection_id());
ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
// Now create new client and server streams with the existing config
// and try the handshake again (0-RTT handshake).
InitializeServer();
server_stream_->set_use_stateless_rejects_if_peer_supported(true);
InitializeFakeClient(/* supports_stateless_rejects= */ true);
client_stream_->CryptoConnect();
// In the stateless case, the second handshake contains a server-nonce, so the
// AsyncStrikeRegisterVerification() case will still succeed (unlike a 0-RTT
// handshake).
AdvanceHandshakeWithFakeClient();
// On the second round, encryption will be established.
EXPECT_TRUE(server_stream_->encryption_established());
EXPECT_TRUE(server_stream_->handshake_confirmed());
EXPECT_EQ(2, server_stream_->num_handshake_messages());
EXPECT_EQ(1, server_stream_->num_handshake_messages_with_server_nonces());
}
TEST_P(QuicCryptoServerStreamTest, NoStatelessRejectIfNoClientSupport) {
ValueRestore<bool> old_flag(&FLAGS_enable_quic_stateless_reject_support,
true);
server_stream_->set_use_stateless_rejects_if_peer_supported(true);
// The server is configured to use stateless rejects, but the client does not
// support it.
InitializeFakeClient(/* supports_stateless_rejects= */ false);
AdvanceHandshakeWithFakeClient();
// Check the server to make the sure the handshake did not succeed.
EXPECT_FALSE(server_stream_->encryption_established());
EXPECT_FALSE(server_stream_->handshake_confirmed());
// Check the client state to make sure that it did not receive a
// server-designated connection id.
QuicCryptoClientConfig::CachedState* client_state =
client_crypto_config_.LookupOrCreate(server_id_);
ASSERT_FALSE(client_state->has_server_designated_connection_id());
ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
}
TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
InitializeFakeClient(/* supports_stateless_rejects= */ false);
// Do a first handshake in order to prime the client config with the server's
// information.
AdvanceHandshakeWithFakeClient();
// Now do another handshake, hopefully in 0-RTT.
DVLOG(1) << "Resetting for 0-RTT handshake attempt";
InitializeFakeClient(/* supports_stateless_rejects= */ false);
InitializeServer();
client_stream_->CryptoConnect();
if (AsyncStrikeRegisterVerification()) {
EXPECT_FALSE(client_stream_->handshake_confirmed());
EXPECT_FALSE(server_stream_->handshake_confirmed());
// Advance the handshake. Expect that the server will be stuck waiting for
// client nonce verification to complete.
pair<size_t, size_t> messages_moved = CryptoTestUtils::AdvanceHandshake(
client_connection_, client_stream_.get(), 0, server_connection_,
server_stream_.get(), 0);
EXPECT_EQ(1u, messages_moved.first);
EXPECT_EQ(0u, messages_moved.second);
EXPECT_EQ(1, strike_register_client_->PendingVerifications());
EXPECT_FALSE(client_stream_->handshake_confirmed());
EXPECT_FALSE(server_stream_->handshake_confirmed());
// The server handshake completes once the nonce verification completes.
strike_register_client_->RunPendingVerifications();
EXPECT_FALSE(client_stream_->handshake_confirmed());
EXPECT_TRUE(server_stream_->handshake_confirmed());
messages_moved = CryptoTestUtils::AdvanceHandshake(
client_connection_, client_stream_.get(), messages_moved.first,
server_connection_, server_stream_.get(), messages_moved.second);
EXPECT_EQ(1u, messages_moved.first);
EXPECT_EQ(1u, messages_moved.second);
EXPECT_TRUE(client_stream_->handshake_confirmed());
EXPECT_TRUE(server_stream_->handshake_confirmed());
} else {
CryptoTestUtils::CommunicateHandshakeMessages(
client_connection_, client_stream_.get(), server_connection_,
server_stream_.get());
}
EXPECT_EQ(1, client_stream_->num_sent_client_hellos());
}
TEST_P(QuicCryptoServerStreamTest, MessageAfterHandshake) {
CompleteCryptoHandshake();
EXPECT_CALL(
*server_connection_,
SendConnectionClose(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE));
message_.set_tag(kCHLO);
ConstructHandshakeMessage();
server_stream_->ProcessRawData(message_data_->data(),
message_data_->length());
}
TEST_P(QuicCryptoServerStreamTest, BadMessageType) {
message_.set_tag(kSHLO);
ConstructHandshakeMessage();
EXPECT_CALL(*server_connection_,
SendConnectionClose(QUIC_INVALID_CRYPTO_MESSAGE_TYPE));
server_stream_->ProcessRawData(message_data_->data(),
message_data_->length());
}
TEST_P(QuicCryptoServerStreamTest, WithoutCertificates) {
server_crypto_config_.SetProofSource(nullptr);
client_options_.dont_verify_certs = true;
// Only 2 client hellos need to be sent in the no-certs case: one to get the
// source-address token and the second to finish.
EXPECT_EQ(2, CompleteCryptoHandshake());
EXPECT_TRUE(server_stream_->encryption_established());
EXPECT_TRUE(server_stream_->handshake_confirmed());
}
TEST_P(QuicCryptoServerStreamTest, ChannelID) {
client_options_.channel_id_enabled = true;
client_options_.channel_id_source_async = false;
// CompleteCryptoHandshake verifies
// server_stream_->crypto_negotiated_params().channel_id is correct.
EXPECT_EQ(2, CompleteCryptoHandshake());
EXPECT_TRUE(server_stream_->encryption_established());
EXPECT_TRUE(server_stream_->handshake_confirmed());
}
TEST_P(QuicCryptoServerStreamTest, ChannelIDAsync) {
client_options_.channel_id_enabled = true;
client_options_.channel_id_source_async = true;
// CompleteCryptoHandshake verifies
// server_stream_->crypto_negotiated_params().channel_id is correct.
EXPECT_EQ(2, CompleteCryptoHandshake());
EXPECT_TRUE(server_stream_->encryption_established());
EXPECT_TRUE(server_stream_->handshake_confirmed());
}
TEST_P(QuicCryptoServerStreamTest, OnlySendSCUPAfterHandshakeComplete) {
// An attempt to send a SCUP before completing handshake should fail.
server_stream_->SendServerConfigUpdate(nullptr);
EXPECT_EQ(0, server_stream_->num_server_config_update_messages_sent());
}
TEST_P(QuicCryptoServerStreamTest, DoesPeerSupportStatelessRejects) {
ConstructHandshakeMessage();
QuicConfig stateless_reject_config = DefaultQuicConfigStatelessRejects();
stateless_reject_config.ToHandshakeMessage(&message_);
EXPECT_TRUE(
QuicCryptoServerStreamPeer::DoesPeerSupportStatelessRejects(message_));
message_.Clear();
QuicConfig stateful_reject_config = DefaultQuicConfig();
stateful_reject_config.ToHandshakeMessage(&message_);
EXPECT_FALSE(
QuicCryptoServerStreamPeer::DoesPeerSupportStatelessRejects(message_));
}
} // namespace
} // namespace test
} // namespace net