blob: 97f7839998ac77f837483d75ef8d0c887326b175 [file] [log] [blame]
// Copyright 2016 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/tools/quic/stateless_rejector.h"
#include <memory>
#include <vector>
#include "net/quic/crypto/crypto_handshake_message.h"
#include "net/quic/crypto/proof_source.h"
#include "net/quic/quic_utils.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_crypto_server_config_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
using std::ostream;
using std::string;
using std::vector;
namespace net {
namespace test {
namespace {
const QuicConnectionId kConnectionId = 42;
const QuicConnectionId kServerDesignateConnectionId = 24;
// All four combinations of the two flags involved.
enum FlagsMode { ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED };
// Test various combinations of QUIC version and flag state.
struct TestParams {
QuicVersion version;
FlagsMode flags;
friend ostream& operator<<(ostream& os, const TestParams& p) {
os << "{ version: " << p.version << " flags: " << p.flags << " }";
return os;
}
};
vector<TestParams> GetTestParams() {
vector<TestParams> params;
for (FlagsMode flags :
{ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED}) {
for (QuicVersion version : QuicSupportedVersions()) {
TestParams param;
param.version = version;
param.flags = flags;
params.push_back(param);
}
}
return params;
}
class StatelessRejectorTest : public ::testing::TestWithParam<TestParams> {
public:
StatelessRejectorTest()
: proof_source_(CryptoTestUtils::ProofSourceForTesting()),
config_(QuicCryptoServerConfig::TESTING,
QuicRandom::GetInstance(),
CryptoTestUtils::ProofSourceForTesting()),
config_peer_(&config_),
compressed_certs_cache_(
QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
rejector_(GetParam().version,
QuicSupportedVersions(),
&config_,
&compressed_certs_cache_,
&clock_,
QuicRandom::GetInstance(),
IPEndPoint(net::test::Loopback4(), 12345),
IPEndPoint(net::test::Loopback4(), 443)) {
FLAGS_enable_quic_stateless_reject_support =
GetParam().flags == ENABLED || GetParam().flags == CHEAP_DISABLED;
FLAGS_quic_use_cheap_stateless_rejects =
GetParam().flags == ENABLED || GetParam().flags == STATELESS_DISABLED;
// Add a new primary config.
std::unique_ptr<CryptoHandshakeMessage> msg(config_.AddDefaultConfig(
QuicRandom::GetInstance(), &clock_, config_options_));
// Save the server config.
scid_hex_ = "#" + QuicUtils::HexEncode(config_peer_.GetPrimaryConfig()->id);
// Encode the QUIC version.
ver_hex_ = QuicUtils::TagToString(QuicVersionToQuicTag(GetParam().version));
// Generate a public value.
char public_value[32];
memset(public_value, 42, sizeof(public_value));
pubs_hex_ = "#" + QuicUtils::HexEncode(public_value, sizeof(public_value));
// Generate a client nonce.
string nonce;
CryptoUtils::GenerateNonce(
clock_.WallNow(), QuicRandom::GetInstance(),
StringPiece(
reinterpret_cast<char*>(config_peer_.GetPrimaryConfig()->orbit),
kOrbitSize),
&nonce);
nonc_hex_ = "#" + QuicUtils::HexEncode(nonce);
// Generate a source address token.
SourceAddressTokens previous_tokens;
IPAddress ip = net::test::Loopback4();
MockRandom rand;
string stk = config_peer_.NewSourceAddressToken(
config_peer_.GetPrimaryConfig()->id, previous_tokens, ip, &rand,
clock_.WallNow(), nullptr);
stk_hex_ = "#" + QuicUtils::HexEncode(stk);
}
protected:
std::unique_ptr<ProofSource> proof_source_;
MockClock clock_;
QuicCryptoServerConfig config_;
QuicCryptoServerConfigPeer config_peer_;
QuicCompressedCertsCache compressed_certs_cache_;
QuicCryptoServerConfig::ConfigOptions config_options_;
StatelessRejector rejector_;
// Values used in CHLO messages
string scid_hex_;
string nonc_hex_;
string pubs_hex_;
string ver_hex_;
string stk_hex_;
};
INSTANTIATE_TEST_CASE_P(Flags,
StatelessRejectorTest,
::testing::ValuesIn(GetTestParams()));
TEST_P(StatelessRejectorTest, InvalidChlo) {
// clang-format off
const CryptoHandshakeMessage client_hello = CryptoTestUtils::Message(
"CHLO",
"COPT", "SREJ",
nullptr);
// clang-format on
rejector_.OnChlo(GetParam().version, kConnectionId,
kServerDesignateConnectionId, client_hello);
if (GetParam().flags != ENABLED || GetParam().version <= QUIC_VERSION_32) {
EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_.state());
return;
}
EXPECT_EQ(StatelessRejector::FAILED, rejector_.state());
EXPECT_EQ(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, rejector_.error());
}
TEST_P(StatelessRejectorTest, ValidChloWithoutSrejSupport) {
// clang-format off
const CryptoHandshakeMessage client_hello = CryptoTestUtils::Message(
"CHLO",
"AEAD", "AESG",
"KEXS", "C255",
"PUBS", pubs_hex_.c_str(),
"NONC", nonc_hex_.c_str(),
"VER\0", ver_hex_.c_str(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
nullptr);
// clang-format on
rejector_.OnChlo(GetParam().version, kConnectionId,
kServerDesignateConnectionId, client_hello);
EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_.state());
}
TEST_P(StatelessRejectorTest, RejectChlo) {
// clang-format off
const CryptoHandshakeMessage client_hello = CryptoTestUtils::Message(
"CHLO",
"AEAD", "AESG",
"KEXS", "C255",
"COPT", "SREJ",
"SCID", scid_hex_.c_str(),
"PUBS", pubs_hex_.c_str(),
"NONC", nonc_hex_.c_str(),
"#004b5453", stk_hex_.c_str(),
"VER\0", ver_hex_.c_str(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
nullptr);
// clang-format on
rejector_.OnChlo(GetParam().version, kConnectionId,
kServerDesignateConnectionId, client_hello);
if (GetParam().flags != ENABLED || GetParam().version <= QUIC_VERSION_32) {
EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_.state());
return;
}
ASSERT_EQ(StatelessRejector::REJECTED, rejector_.state());
const CryptoHandshakeMessage& reply = rejector_.reply();
EXPECT_EQ(kSREJ, reply.tag());
const uint32_t* reject_reasons;
size_t num_reject_reasons;
EXPECT_EQ(QUIC_NO_ERROR,
reply.GetTaglist(kRREJ, &reject_reasons, &num_reject_reasons));
EXPECT_EQ(1u, num_reject_reasons);
EXPECT_EQ(INVALID_EXPECTED_LEAF_CERTIFICATE,
static_cast<HandshakeFailureReason>(reject_reasons[0]));
}
TEST_P(StatelessRejectorTest, AcceptChlo) {
const uint64_t xlct = CryptoTestUtils::LeafCertHashForTesting();
const string xlct_hex =
"#" +
QuicUtils::HexEncode(reinterpret_cast<const char*>(&xlct), sizeof(xlct));
// clang-format off
const CryptoHandshakeMessage client_hello = CryptoTestUtils::Message(
"CHLO",
"AEAD", "AESG",
"KEXS", "C255",
"COPT", "SREJ",
"SCID", scid_hex_.c_str(),
"PUBS", pubs_hex_.c_str(),
"NONC", nonc_hex_.c_str(),
"#004b5453", stk_hex_.c_str(),
"VER\0", ver_hex_.c_str(),
"XLCT", xlct_hex.c_str(),
"$padding", static_cast<int>(kClientHelloMinimumSize),
nullptr);
// clang-format on
rejector_.OnChlo(GetParam().version, kConnectionId,
kServerDesignateConnectionId, client_hello);
if (GetParam().flags != ENABLED || GetParam().version <= QUIC_VERSION_32) {
EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_.state());
return;
}
EXPECT_EQ(StatelessRejector::ACCEPTED, rejector_.state());
}
} // namespace
} // namespace test
} // namespace net