Project import generated by Copybara.
PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/stateless_rejector_test.cc b/quic/core/stateless_rejector_test.cc
new file mode 100644
index 0000000..19eef64
--- /dev/null
+++ b/quic/core/stateless_rejector_test.cc
@@ -0,0 +1,290 @@
+// 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/third_party/quiche/src/quic/core/stateless_rejector.h"
+
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+QuicConnectionId TestServerDesignatedConnectionId() {
+ return TestConnectionId(24);
+}
+
+// All four combinations of the two flags involved.
+enum FlagsMode { ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED };
+
+const char* FlagsModeToString(FlagsMode mode) {
+ switch (mode) {
+ case ENABLED:
+ return "ENABLED";
+ case STATELESS_DISABLED:
+ return "STATELESS_DISABLED";
+ case CHEAP_DISABLED:
+ return "CHEAP_DISABLED";
+ case BOTH_DISABLED:
+ return "BOTH_DISABLED";
+ default:
+ QUIC_DLOG(FATAL) << "Unexpected FlagsMode";
+ return nullptr;
+ }
+}
+
+// Test various combinations of QUIC version and flag state.
+struct TestParams {
+ ParsedQuicVersion version = UnsupportedQuicVersion();
+ FlagsMode flags;
+};
+
+QuicString TestParamToString(const testing::TestParamInfo<TestParams>& params) {
+ return QuicStrCat("v", ParsedQuicVersionToString(params.param.version), "_",
+ FlagsModeToString(params.param.flags));
+}
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ for (FlagsMode flags :
+ {ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED}) {
+ for (ParsedQuicVersion version : AllSupportedVersions()) {
+ TestParams param;
+ param.version = version;
+ param.flags = flags;
+ params.push_back(param);
+ }
+ }
+ return params;
+}
+
+class StatelessRejectorTest : public QuicTestWithParam<TestParams> {
+ public:
+ StatelessRejectorTest()
+ : proof_source_(crypto_test_utils::ProofSourceForTesting()),
+ config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ config_peer_(&config_),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ rejector_(QuicMakeUnique<StatelessRejector>(
+ GetParam().version,
+ AllSupportedVersions(),
+ &config_,
+ &compressed_certs_cache_,
+ &clock_,
+ QuicRandom::GetInstance(),
+ kDefaultMaxPacketSize,
+ QuicSocketAddress(QuicIpAddress::Loopback4(), 12345),
+ QuicSocketAddress(QuicIpAddress::Loopback4(), 443))) {
+ SetQuicReloadableFlag(
+ enable_quic_stateless_reject_support,
+ GetParam().flags == ENABLED || GetParam().flags == CHEAP_DISABLED);
+ SetQuicReloadableFlag(
+ 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_ =
+ "#" + QuicTextUtils::HexEncode(config_peer_.GetPrimaryConfig()->id);
+
+ // Encode the QUIC version.
+ ver_hex_ = ParsedQuicVersionToString(GetParam().version);
+
+ // Generate a public value.
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+ pubs_hex_ =
+ "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value));
+
+ // Generate a client nonce.
+ QuicString nonce;
+ CryptoUtils::GenerateNonce(
+ clock_.WallNow(), QuicRandom::GetInstance(),
+ QuicStringPiece(
+ reinterpret_cast<char*>(config_peer_.GetPrimaryConfig()->orbit),
+ kOrbitSize),
+ &nonce);
+ nonc_hex_ = "#" + QuicTextUtils::HexEncode(nonce);
+
+ // Generate a source address token.
+ SourceAddressTokens previous_tokens;
+ QuicIpAddress ip = QuicIpAddress::Loopback4();
+ MockRandom rand;
+ QuicString stk = config_peer_.NewSourceAddressToken(
+ config_peer_.GetPrimaryConfig()->id, previous_tokens, ip, &rand,
+ clock_.WallNow(), nullptr);
+ stk_hex_ = "#" + QuicTextUtils::HexEncode(stk);
+ }
+
+ protected:
+ class ProcessDoneCallback : public StatelessRejector::ProcessDoneCallback {
+ public:
+ explicit ProcessDoneCallback(StatelessRejectorTest* test) : test_(test) {}
+ void Run(std::unique_ptr<StatelessRejector> rejector) override {
+ test_->rejector_ = std::move(rejector);
+ }
+
+ private:
+ StatelessRejectorTest* test_;
+ };
+
+ std::unique_ptr<ProofSource> proof_source_;
+ MockClock clock_;
+ QuicCryptoServerConfig config_;
+ QuicCryptoServerConfigPeer config_peer_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicCryptoServerConfig::ConfigOptions config_options_;
+ std::unique_ptr<StatelessRejector> rejector_;
+
+ // Values used in CHLO messages
+ QuicString scid_hex_;
+ QuicString nonc_hex_;
+ QuicString pubs_hex_;
+ QuicString ver_hex_;
+ QuicString stk_hex_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Flags, StatelessRejectorTest,
+ ::testing::ValuesIn(GetTestParams()),
+ TestParamToString);
+
+TEST_P(StatelessRejectorTest, InvalidChlo) {
+ // clang-format off
+ const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"COPT", "SREJ"}});
+ // clang-format on
+ rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
+ TestServerDesignatedConnectionId(), client_hello);
+
+ if (GetParam().flags != ENABLED) {
+ EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
+ return;
+ }
+
+ // The StatelessRejector is undecided - proceed with async processing
+ ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state());
+ StatelessRejector::Process(std::move(rejector_),
+ QuicMakeUnique<ProcessDoneCallback>(this));
+
+ 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 = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pubs_hex_},
+ {"NONC", nonc_hex_},
+ {"VER\0", ver_hex_}},
+ kClientHelloMinimumSize);
+ // clang-format on
+
+ rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
+ TestServerDesignatedConnectionId(), client_hello);
+ EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
+}
+
+TEST_P(StatelessRejectorTest, RejectChlo) {
+ // clang-format off
+ const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"COPT", "SREJ"},
+ {"SCID", scid_hex_},
+ {"PUBS", pubs_hex_},
+ {"NONC", nonc_hex_},
+ {"#004b5453", stk_hex_},
+ {"VER\0", ver_hex_}},
+ kClientHelloMinimumSize);
+ // clang-format on
+
+ rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
+ TestServerDesignatedConnectionId(), client_hello);
+ if (GetParam().flags != ENABLED) {
+ EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
+ return;
+ }
+
+ // The StatelessRejector is undecided - proceed with async processing
+ ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state());
+ StatelessRejector::Process(std::move(rejector_),
+ QuicMakeUnique<ProcessDoneCallback>(this));
+
+ ASSERT_EQ(StatelessRejector::REJECTED, rejector_->state());
+ const CryptoHandshakeMessage& reply = rejector_->reply();
+ EXPECT_EQ(kSREJ, reply.tag());
+ QuicTagVector reject_reasons;
+ EXPECT_EQ(QUIC_NO_ERROR, reply.GetTaglist(kRREJ, &reject_reasons));
+ EXPECT_EQ(1u, reject_reasons.size());
+ EXPECT_EQ(INVALID_EXPECTED_LEAF_CERTIFICATE,
+ static_cast<HandshakeFailureReason>(reject_reasons[0]));
+}
+
+TEST_P(StatelessRejectorTest, AcceptChlo) {
+ const uint64_t xlct = crypto_test_utils::LeafCertHashForTesting();
+ const QuicString xlct_hex =
+ "#" + QuicTextUtils::HexEncode(reinterpret_cast<const char*>(&xlct),
+ sizeof(xlct));
+ // clang-format off
+ const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"COPT", "SREJ"},
+ {"SCID", scid_hex_},
+ {"PUBS", pubs_hex_},
+ {"NONC", nonc_hex_},
+ {"#004b5453", stk_hex_},
+ {"VER\0", ver_hex_},
+ {"XLCT", xlct_hex}},
+ kClientHelloMinimumSize);
+ // clang-format on
+
+ rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
+ TestServerDesignatedConnectionId(), client_hello);
+ if (GetParam().flags != ENABLED) {
+ EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
+ return;
+ }
+
+ // The StatelessRejector is undecided - proceed with async processing
+ ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state());
+ StatelessRejector::Process(std::move(rejector_),
+ QuicMakeUnique<ProcessDoneCallback>(this));
+
+ EXPECT_EQ(StatelessRejector::ACCEPTED, rejector_->state());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic