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