/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "secerr.h"
#include "ssl.h"

#include "gtest_utils.h"
#include "tls_connect.h"
#include "util.h"

namespace nss_test {

const uint8_t kTlsGreaseExtensionMessages[] = {kTlsHandshakeEncryptedExtensions,
                                               kTlsHandshakeCertificate};

const uint16_t kTlsGreaseValues[] = {
    0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a,
    0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa};

const uint8_t kTlsGreasePskValues[] = {0x0B, 0x2A, 0x49, 0x68,
                                       0x87, 0xA6, 0xC5, 0xE4};

size_t countGreaseInBuffer(const DataBuffer& list) {
  if (!list.len()) {
    return 0;
  }
  size_t occurrence = 0;
  for (uint16_t greaseVal : kTlsGreaseValues) {
    for (size_t i = 0; i < (list.len() - 1); i += 2) {
      uint16_t sample = list.data()[i + 1] + (list.data()[i] << 8);
      if (greaseVal == sample) {
        occurrence++;
      }
    }
  }
  return occurrence;
}

class GreasePresenceAbsenceTestBase : public TlsConnectTestBase {
 public:
  GreasePresenceAbsenceTestBase(SSLProtocolVariant variant, uint16_t version,
                                bool shouldGrease)
      : TlsConnectTestBase(variant, version), set_grease_(shouldGrease){};

  void SetupGrease() {
    EnsureTlsSetup();
    ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, set_grease_),
              SECSuccess);
    ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, set_grease_),
              SECSuccess);
  }

  bool expectGrease() {
    return set_grease_ && version_ >= SSL_LIBRARY_VERSION_TLS_1_3;
  }

  void checkGreasePresence(const int ifEnabled, const int ifDisabled,
                           const DataBuffer& buffer) {
    size_t expected = expectGrease() ? size_t(ifEnabled) : size_t(ifDisabled);
    EXPECT_EQ(expected, countGreaseInBuffer(buffer));
  }

 private:
  bool set_grease_;
};

class GreasePresenceAbsenceTestAllVersions
    : public GreasePresenceAbsenceTestBase,
      public ::testing::WithParamInterface<
          std::tuple<SSLProtocolVariant, uint16_t, bool>> {
 public:
  GreasePresenceAbsenceTestAllVersions()
      : GreasePresenceAbsenceTestBase(std::get<0>(GetParam()),
                                      std::get<1>(GetParam()),
                                      std::get<2>(GetParam())){};
};

// Varies stream/datagram, TLS Version and whether GREASE is enabled
INSTANTIATE_TEST_SUITE_P(GreaseTests, GreasePresenceAbsenceTestAllVersions,
                         ::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
                                            TlsConnectTestBase::kTlsV11Plus,
                                            ::testing::Values(true, false)));

// Varies whether GREASE is enabled for TLS13 only
class GreasePresenceAbsenceTestTlsStream13
    : public GreasePresenceAbsenceTestBase,
      public ::testing::WithParamInterface<bool> {
 public:
  GreasePresenceAbsenceTestTlsStream13()
      : GreasePresenceAbsenceTestBase(
            ssl_variant_stream, SSL_LIBRARY_VERSION_TLS_1_3, GetParam()){};
};

INSTANTIATE_TEST_SUITE_P(GreaseTests, GreasePresenceAbsenceTestTlsStream13,
                         ::testing::Values(true, false));

// These tests check for the presence / absence of GREASE values in the various
// positions that we are permitted to add them. For positions which existed in
// prior versions of TLS, we check that enabling GREASE is only effective when
// negotiating TLS1.3 or higher and that disabling GREASE results in the absence
// of any GREASE values.
// For positions that specific to TLS1.3, we only check that enabling/disabling
// GREASE results in the correct presence/absence of the GREASE value.

TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseCiphersuites) {
  SetupGrease();

  auto ch1 = MakeTlsFilter<ClientHelloCiphersuiteCapture>(client_);
  Connect();
  EXPECT_TRUE(ch1->captured());

  checkGreasePresence(1, 0, ch1->contents());
}

TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseNamedGroups) {
  SetupGrease();

  auto ch1 =
      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_supported_groups_xtn);
  Connect();
  EXPECT_TRUE(ch1->captured());

  checkGreasePresence(1, 0, ch1->extension());
}

TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseKeyShare) {
  SetupGrease();

  auto ch1 =
      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_key_share_xtn);
  // filters only work with particular groups
  server_->ConfigNamedGroups(kNonPQDHEGroups);
  client_->ConfigNamedGroups(kNonPQDHEGroups);
  Connect();
  EXPECT_TRUE((version_ >= SSL_LIBRARY_VERSION_TLS_1_3) == ch1->captured());

  // Parse the key_share extension structurally: only count GREASE values in
  // the group ID fields. Scanning the raw buffer with countGreaseInBuffer
  // would spuriously count GREASE patterns in the random key_exchange bytes.
  size_t greaseCount = 0;
  if (ch1->captured()) {
    TlsParser extParser(ch1->extension());
    DataBuffer keyShares;
    ASSERT_TRUE(extParser.ReadVariable(&keyShares, 2));
    TlsParser shareParser(keyShares);
    while (shareParser.remaining()) {
      uint32_t group;
      DataBuffer keyExchange;
      ASSERT_TRUE(shareParser.Read(&group, 2));
      ASSERT_TRUE(shareParser.ReadVariable(&keyExchange, 2));
      for (uint16_t greaseVal : kTlsGreaseValues) {
        if (static_cast<uint16_t>(group) == greaseVal) {
          greaseCount++;
        }
      }
    }
  }
  size_t expected = expectGrease() ? size_t(1) : size_t(0);
  EXPECT_EQ(expected, greaseCount);
}

TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseSigAlg) {
  SetupGrease();

  auto ch1 =
      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn);
  Connect();
  EXPECT_TRUE((version_ >= SSL_LIBRARY_VERSION_TLS_1_2) == ch1->captured());

  checkGreasePresence(1, 0, ch1->extension());
}

TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseSupportedVersions) {
  SetupGrease();

  auto ch1 = MakeTlsFilter<TlsExtensionCapture>(
      client_, ssl_tls13_supported_versions_xtn);
  Connect();
  EXPECT_TRUE((version_ >= SSL_LIBRARY_VERSION_TLS_1_3) == ch1->captured());

  // Supported Versions have a 1 byte length field.
  TlsParser extParser(ch1->extension());
  DataBuffer versions;
  extParser.ReadVariable(&versions, 1);

  checkGreasePresence(1, 0, versions);
}

TEST_P(GreasePresenceAbsenceTestTlsStream13, ClientGreasePskExchange) {
  SetupGrease();

  auto ch1 = MakeTlsFilter<TlsExtensionCapture>(
      client_, ssl_tls13_psk_key_exchange_modes_xtn);
  Connect();
  EXPECT_TRUE(ch1->captured());

  // PSK Exchange Modes have a 1 byte length field
  TlsParser extParser(ch1->extension());
  DataBuffer modes;
  extParser.ReadVariable(&modes, 1);

  // Scan for single byte GREASE PSK Values
  size_t numGrease = 0;
  for (uint8_t greaseVal : kTlsGreasePskValues) {
    for (unsigned long i = 0; i < modes.len(); i++) {
      if (greaseVal == modes.data()[i]) {
        numGrease++;
      }
    }
  }

  EXPECT_EQ(expectGrease() ? size_t(1) : size_t(0), numGrease);
}

TEST_P(GreasePresenceAbsenceTestAllVersions, ClientGreaseAlpn) {
  SetupGrease();
  EnableAlpn();

  auto ch1 =
      MakeTlsFilter<TlsExtensionCapture>(client_, ssl_app_layer_protocol_xtn);
  Connect();
  EXPECT_TRUE((version_ >= SSL_LIBRARY_VERSION_TLS_1_1) == ch1->captured());

  // ALPN Xtns have a redundant two-byte length
  TlsParser alpnParser(ch1->extension());
  alpnParser.Skip(2);  // Skip the length
  DataBuffer alpnEntry;

  // Each ALPN entry has a single byte length prefixed.
  size_t greaseAlpnEntrys = 0;
  while (alpnParser.remaining()) {
    alpnParser.ReadVariable(&alpnEntry, 1);
    if (alpnEntry.len() == 2) {
      greaseAlpnEntrys += countGreaseInBuffer(alpnEntry);
    }
  }

  EXPECT_EQ(expectGrease() ? size_t(1) : size_t(0), greaseAlpnEntrys);
}

TEST_P(GreasePresenceAbsenceTestAllVersions, GreaseClientHelloExtension) {
  SetupGrease();

  auto ch1 =
      MakeTlsFilter<TlsHandshakeRecorder>(client_, kTlsHandshakeClientHello);
  Connect();
  EXPECT_TRUE(ch1->buffer().len() > 0);

  TlsParser extParser(ch1->buffer());
  EXPECT_TRUE(extParser.Skip(2 + 32));     // Version + Random
  EXPECT_TRUE(extParser.SkipVariable(1));  // Session ID
  if (variant_ == ssl_variant_datagram) {
    EXPECT_TRUE(extParser.SkipVariable(1));  // Cookie
  }
  EXPECT_TRUE(extParser.SkipVariable(2));  // Ciphersuites
  EXPECT_TRUE(extParser.SkipVariable(1));  // Compression Methods
  EXPECT_TRUE(extParser.Skip(2));          // Extension Lengths

  // Scan for a 1-byte and a 0-byte extension.
  uint32_t extType;
  DataBuffer extBuf;
  bool foundSmall = false;
  bool foundLarge = false;
  size_t numFound = 0;
  while (extParser.remaining()) {
    extParser.Read(&extType, 2);
    extParser.ReadVariable(&extBuf, 2);
    for (uint16_t greaseVal : kTlsGreaseValues) {
      if (greaseVal == extType) {
        numFound++;
        foundSmall |= extBuf.len() == 0;
        foundLarge |= extBuf.len() > 0;
      }
    }
  }

  EXPECT_EQ(foundSmall, expectGrease());
  EXPECT_EQ(foundLarge, expectGrease());
  EXPECT_EQ(numFound, expectGrease() ? size_t(2) : size_t(0));
}

TEST_P(GreasePresenceAbsenceTestTlsStream13, GreaseCertificateRequestSigAlg) {
  SetupGrease();
  client_->SetupClientAuth();
  server_->RequestClientAuth(true);

  auto cr =
      MakeTlsFilter<TlsExtensionCapture>(server_, ssl_signature_algorithms_xtn);
  cr->SetHandshakeTypes({kTlsHandshakeCertificateRequest});
  cr->EnableDecryption();
  Connect();
  EXPECT_TRUE(cr->captured());

  checkGreasePresence(1, 0, cr->extension());
}

TEST_P(GreasePresenceAbsenceTestTlsStream13,
       GreaseCertificateRequestExtension) {
  SetupGrease();
  client_->SetupClientAuth();
  server_->RequestClientAuth(true);

  auto cr = MakeTlsFilter<TlsHandshakeRecorder>(
      server_, kTlsHandshakeCertificateRequest);
  cr->EnableDecryption();
  Connect();
  EXPECT_TRUE(cr->buffer().len() > 0);

  TlsParser extParser(cr->buffer());
  EXPECT_TRUE(extParser.SkipVariable(1));  // Context
  EXPECT_TRUE(extParser.Skip(2));          // Extension Lengths

  uint32_t extType;
  DataBuffer extBuf;
  bool found = false;
  // Scan for a single, empty extension
  while (extParser.remaining()) {
    extParser.Read(&extType, 2);
    extParser.ReadVariable(&extBuf, 2);
    for (uint16_t greaseVal : kTlsGreaseValues) {
      if (greaseVal == extType) {
        EXPECT_TRUE(!found);
        EXPECT_EQ(extBuf.len(), size_t(0));
        found = true;
      }
    }
  }

  EXPECT_EQ(expectGrease(), found);
}

TEST_P(GreasePresenceAbsenceTestTlsStream13, GreaseNewSessionTicketExtension) {
  SetupGrease();

  auto nst = MakeTlsFilter<TlsHandshakeRecorder>(server_,
                                                 kTlsHandshakeNewSessionTicket);
  nst->EnableDecryption();
  Connect();
  EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), nullptr, 0));
  EXPECT_TRUE(nst->buffer().len() > 0);

  TlsParser extParser(nst->buffer());
  EXPECT_TRUE(extParser.Skip(4));          // lifetime
  EXPECT_TRUE(extParser.Skip(4));          // age
  EXPECT_TRUE(extParser.SkipVariable(1));  // Nonce
  EXPECT_TRUE(extParser.SkipVariable(2));  // Ticket
  EXPECT_TRUE(extParser.Skip(2));          // Extension Length

  uint32_t extType;
  DataBuffer extBuf;
  bool found = false;
  // Scan for a single, empty extension
  while (extParser.remaining()) {
    extParser.Read(&extType, 2);
    extParser.ReadVariable(&extBuf, 2);
    for (uint16_t greaseVal : kTlsGreaseValues) {
      if (greaseVal == extType) {
        EXPECT_TRUE(!found);
        EXPECT_EQ(extBuf.len(), size_t(0));
        found = true;
      }
    }
  }

  EXPECT_EQ(expectGrease(), found);
}

// Generic Client GREASE test
TEST_P(TlsConnectGeneric, ClientGrease) {
  EnsureTlsSetup();
  ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  Connect();
}

// Generic Server GREASE test
TEST_P(TlsConnectGeneric, ServerGrease) {
  EnsureTlsSetup();
  ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  Connect();
}

// Generic GREASE test
TEST_P(TlsConnectGeneric, Grease) {
  EnsureTlsSetup();
  ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  Connect();
}

// Check that GREASE values can be correctly reconstructed after HRR.
TEST_P(TlsConnectGeneric, GreaseHRR) {
  EnsureTlsSetup();
  const std::vector<SSLNamedGroup> client_groups = {
      ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1};
  const std::vector<SSLNamedGroup> server_groups = {
      ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, ssl_grp_ec_curve25519};
  client_->ConfigNamedGroups(client_groups);
  server_->ConfigNamedGroups(server_groups);
  ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  Connect();
}

// Check that GREASE additions interact correctly with psk-only handshake.
TEST_F(TlsConnectStreamTls13, GreasePsk) {
  EnsureTlsSetup();
  ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);

  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
  const uint8_t kPskDummyVal_[16] = {0x01, 0x02, 0x03, 0x04, 0x05,
                                     0x06, 0x07, 0x08, 0x09, 0x0a,
                                     0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
  SECItem psk_item;
  psk_item.type = siBuffer;
  psk_item.len = sizeof(kPskDummyVal_);
  psk_item.data = const_cast<uint8_t*>(kPskDummyVal_);
  PK11SymKey* key =
      PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap,
                        CKA_DERIVE, &psk_item, NULL);

  ScopedPK11SymKey scoped_psk_(key);
  const std::string kPskDummyLabel_ = "NSS PSK GTEST label";
  const SSLHashType kPskHash_ = ssl_hash_sha384;
  AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_);

  Connect();
  SendReceive();
  CheckKeys(ssl_auth_psk, ssl_sig_none);
}

// Test that ECH and GREASE work together successfully
TEST_F(TlsConnectStreamTls13, GreaseAndECH) {
  EnsureTlsSetup();
  SetupEch(client_, server_);
  ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  Connect();
}

// Test that TLS12 Server handles Client GREASE correctly
TEST_F(TlsConnectTest, GreaseTLS12Server) {
  EnsureTlsSetup();
  ASSERT_EQ(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                           SSL_LIBRARY_VERSION_TLS_1_2);
  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                           SSL_LIBRARY_VERSION_TLS_1_3);
  Connect();
}

// Test that TLS12 Client handles Server GREASE correctly
TEST_F(TlsConnectTest, GreaseTLS12Client) {
  EnsureTlsSetup();
  ASSERT_EQ(SSL_OptionSet(server_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE),
            SECSuccess);
  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                           SSL_LIBRARY_VERSION_TLS_1_3);
  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                           SSL_LIBRARY_VERSION_TLS_1_2);
  Connect();
}

class GreaseOnlyTestStreamTls13 : public TlsConnectStreamTls13 {
 public:
  GreaseOnlyTestStreamTls13() : TlsConnectStreamTls13() {}

  void ConnectWithCustomChExpectFail(const std::string& ch,
                                     uint8_t server_alert, uint32_t server_code,
                                     uint32_t client_code) {
    std::vector<uint8_t> ch_vec = hex_string_to_bytes(ch);
    DataBuffer ch_buf;
    EnsureTlsSetup();

    TlsAgentTestBase::MakeRecord(variant_, ssl_ct_handshake,
                                 SSL_LIBRARY_VERSION_TLS_1_3, ch_vec.data(),
                                 ch_vec.size(), &ch_buf, 0);
    StartConnect();
    client_->SendDirect(ch_buf);
    ExpectAlert(server_, server_alert);
    server_->Handshake();
    server_->CheckErrorCode(server_code);
    client_->ExpectReceiveAlert(server_alert, kTlsAlertFatal);
    client_->Handshake();
    client_->CheckErrorCode(client_code);
  }
};

// Client: Offer only GREASE CipherSuite value
TEST_F(GreaseOnlyTestStreamTls13, GreaseOnlyClientCipherSuite) {
  // 0xdada
  std::string ch =
      "010000b003038afacda2963358e98f464f3ff0680ed3a9d382a8c3eac5e5604f5721add9"
      "855c000002dada010000850000000b0009000006736572766572ff01000100000a001400"
      "12001d00170018001901000101010201030104003300260024001d0020683668992de470"
      "38660ee37bafc7392b05b8a94402ea1f3463ad3cfd7a694a46002b0003020304000d0018"
      "001604030503060302030804080508060401050106010201002d00020101001c0002400"
      "1";

  ConnectWithCustomChExpectFail(ch, kTlsAlertHandshakeFailure,
                                SSL_ERROR_NO_CYPHER_OVERLAP,
                                SSL_ERROR_NO_CYPHER_OVERLAP);
}

// Client: Offer only GREASE SupportedGroups value
TEST_F(GreaseOnlyTestStreamTls13, GreaseOnlyClientSupportedGroup) {
  // 0x3a3a
  std::string ch =
      "010000a40303484a4e14f547404da6115d7f73bbb0f1c9d65e66ac073dee6c4a62f72de9"
      "a36f000006130113031302010000750000000b0009000006736572766572ff0100010000"
      "0a000400023a3a003300260024001d0020e75cb8e217c95176954e8b5fb95843882462ce"
      "2cd3fcfe67cf31463a05ea3d57002b0003020304000d0018001604030503060302030804"
      "080508060401050106010201002d00020101001c00024001";

  ConnectWithCustomChExpectFail(ch, kTlsAlertHandshakeFailure,
                                SSL_ERROR_NO_CYPHER_OVERLAP,
                                SSL_ERROR_NO_CYPHER_OVERLAP);
}

// Client: Offer only GREASE SigAlgs value
TEST_F(GreaseOnlyTestStreamTls13, GreaseOnlyClientSignatureAlgorithm) {
  // 0x8a8a
  std::string ch =
      "010000a00303dfd8e2438a8d1b9f48d921dfc08959108807bd1105238bb3da2a2a8e3db0"
      "6990000006130113031302010000710000000b0009000006736572766572ff0100010000"
      "0a00140012001d00170018001901000101010201030104003300260024001d002074bb2c"
      "94996d3ffc7ae5792f0c3c58676358a85ea304cd029fa3d6551013b333002b0003020304"
      "000d000400028a8a002d00020101001c00024001";

  ConnectWithCustomChExpectFail(ch, kTlsAlertHandshakeFailure,
                                SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM,
                                SSL_ERROR_NO_CYPHER_OVERLAP);
}

// Client: Offer only GREASE SupportedVersions value
TEST_F(GreaseOnlyTestStreamTls13, GreaseOnlyClientSupportedVersion) {
  // 0xeaea
  std::string ch =
      "010000b203037e3618abae0dd0b3f06a504c47354551d1d5be36e9c3e1eac9c139c246b1"
      "66da000006130113031302010000830000000b0009000006736572766572ff0100010000"
      "0a00140012001d00170018001901000101010201030104003300260024001d00206b1816"
      "577ff2e69d4d2661419150eaefa0328ffd396425cf1733ec06536b4e55002b000100000d"
      "0018001604030503060302030804080508060401050106010201002d00020101001c0002"
      "4001";

  ConnectWithCustomChExpectFail(ch, kTlsAlertIllegalParameter,
                                SSL_ERROR_RX_MALFORMED_CLIENT_HELLO,
                                SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}

class GreaseTestStreamTls12
    : public TlsConnectStreamTls12,
      public ::testing::WithParamInterface<uint16_t /* GREASE */> {
 public:
  GreaseTestStreamTls12() : TlsConnectStreamTls12(), grease_(GetParam()){};

  void ConnectExpectSigAlgFail() {
    client_->ExpectSendAlert(kTlsAlertIllegalParameter);
    server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
    ConnectExpectFail();
    client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
    server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
  }

 protected:
  uint16_t grease_;
};

class TlsCertificateRequestSigAlgSetterFilter : public TlsHandshakeFilter {
 public:
  TlsCertificateRequestSigAlgSetterFilter(const std::shared_ptr<TlsAgent>& a,
                                          uint16_t sigAlg)
      : TlsHandshakeFilter(a, {kTlsHandshakeCertificateRequest}),
        sigAlg_(sigAlg) {}
  virtual PacketFilter::Action FilterHandshake(
      const TlsHandshakeFilter::HandshakeHeader& header,
      const DataBuffer& input, DataBuffer* output) {
    TlsParser parser(input);
    DataBuffer cert_types;
    if (!parser.ReadVariable(&cert_types, 1)) {
      ADD_FAILURE();
      return KEEP;
    }

    if (!parser.SkipVariable(2)) {
      ADD_FAILURE();
      return KEEP;
    }

    DataBuffer cas;
    if (!parser.ReadVariable(&cas, 2)) {
      ADD_FAILURE();
      return KEEP;
    }

    size_t idx = 0;

    // Write certificate types.
    idx = output->Write(idx, cert_types.len(), 1);
    idx = output->Write(idx, cert_types);

    // Write signature algorithm.
    idx = output->Write(idx, sizeof(sigAlg_), 2);
    idx = output->Write(idx, sigAlg_, 2);

    // Write certificate authorities.
    idx = output->Write(idx, cas.len(), 2);
    idx = output->Write(idx, cas);

    return CHANGE;
  }

 private:
  uint16_t sigAlg_;
};

// Server: Offer only GREASE CertificateRequest SigAlg value
TEST_P(GreaseTestStreamTls12, GreaseOnlyServerTLS12CertificateRequestSigAlg) {
  EnsureTlsSetup();
  client_->SetupClientAuth();
  server_->RequestClientAuth(true);
  MakeTlsFilter<TlsCertificateRequestSigAlgSetterFilter>(server_, grease_);

  client_->ExpectSendAlert(kTlsAlertHandshakeFailure);
  server_->ExpectReceiveAlert(kTlsAlertHandshakeFailure);
  ConnectExpectFail();
  server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
  client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
}

// Illegally GREASE ServerKeyExchange ECC SignatureAlgorithm
TEST_P(GreaseTestStreamTls12, GreasedTLS12ServerKexEccSigAlg) {
  MakeTlsFilter<ECCServerKEXSigAlgReplacer>(server_, grease_);
  EnableSomeEcdhCiphers();

  client_->ExpectSendAlert(kTlsAlertIllegalParameter);
  server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
  ConnectExpectFail();
  client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
  server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}

// Illegally GREASE ServerKeyExchange DHE SignatureAlgorithm
TEST_P(GreaseTestStreamTls12, GreasedTLS12ServerKexDheSigAlg) {
  MakeTlsFilter<DHEServerKEXSigAlgReplacer>(server_, grease_);
  EnableOnlyDheCiphers();

  client_->ExpectSendAlert(kTlsAlertIllegalParameter);
  server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
  ConnectExpectFail();
  client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
  server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}

// Illegally GREASE ServerKeyExchange ECDHE NamedCurve
TEST_P(GreaseTestStreamTls12, GreasedTLS12ServerKexEcdheNamedCurve) {
  MakeTlsFilter<ECCServerKEXNamedCurveReplacer>(server_, grease_);
  EnableSomeEcdhCiphers();

  client_->ExpectSendAlert(kTlsAlertHandshakeFailure);
  server_->ExpectReceiveAlert(kTlsAlertHandshakeFailure);
  ConnectExpectFail();
  server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
  client_->CheckErrorCode(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
}

// Illegally GREASE TLS12 Client CertificateVerify SignatureAlgorithm
TEST_P(GreaseTestStreamTls12, GreasedTLS12ClientCertificateVerifySigAlg) {
  client_->SetupClientAuth();
  server_->RequestClientAuth(true);
  MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(client_, grease_);

  server_->ExpectSendAlert(kTlsAlertIllegalParameter);
  client_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
  ConnectExpectFail();
  client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
  server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
}

class GreaseTestStreamTls13
    : public TlsConnectStreamTls13,
      public ::testing::WithParamInterface<uint16_t /* GREASE */> {
 public:
  GreaseTestStreamTls13() : grease_(GetParam()){};

 protected:
  uint16_t grease_;
};

// Illegally GREASE TLS13 Client CertificateVerify SignatureAlgorithm
TEST_P(GreaseTestStreamTls13, GreasedTLS13ClientCertificateVerifySigAlg) {
  client_->SetupClientAuth();
  server_->RequestClientAuth(true);
  auto filter =
      MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(client_, grease_);
  filter->EnableDecryption();

  server_->ExpectSendAlert(kTlsAlertIllegalParameter);
  client_->ExpectReceiveAlert(kTlsAlertIllegalParameter);

  // Manually trigger handshake to avoid race conditions
  StartConnect();
  client_->Handshake();
  server_->Handshake();
  client_->Handshake();
  server_->Handshake();
  client_->Handshake();

  server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CERT_VERIFY);
  client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}

// Illegally GREASE TLS13 Server CertificateVerify SignatureAlgorithm
TEST_P(GreaseTestStreamTls13, GreasedTLS13ServerCertificateVerifySigAlg) {
  EnsureTlsSetup();
  auto filter =
      MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(server_, grease_);
  filter->EnableDecryption();

  client_->ExpectSendAlert(kTlsAlertIllegalParameter);
  server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
  ConnectExpectFail();
  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CERT_VERIFY);
}

// Illegally GREASE HelloRetryRequest version value
TEST_P(GreaseTestStreamTls13, GreasedHelloRetryRequestVersion) {
  EnsureTlsSetup();
  // Trigger HelloRetryRequest
  MakeTlsFilter<TlsExtensionDropper>(client_, ssl_tls13_key_share_xtn);
  auto filter = MakeTlsFilter<TlsMessageVersionSetter>(
      server_, kTlsHandshakeHelloRetryRequest, grease_);
  filter->EnableDecryption();

  client_->ExpectSendAlert(kTlsAlertIllegalParameter);
  server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
  ConnectExpectFail();
  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
  server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
}

class GreaseTestStreamTls123
    : public TlsConnectTestBase,
      public ::testing::WithParamInterface<
          std::tuple<uint16_t /* version */, uint16_t /* GREASE */>> {
 public:
  GreaseTestStreamTls123()
      : TlsConnectTestBase(ssl_variant_stream, std::get<0>(GetParam())),
        grease_(std::get<1>(GetParam())){};

  void ConnectExpectIllegalGreaseFail() {
    client_->ExpectSendAlert(kTlsAlertIllegalParameter);
    if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
      // Server expects handshake but receives encrypted alert.
      server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
    } else {
      server_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
    }
    ConnectExpectFail();
  }

 protected:
  uint16_t grease_;
};

// Illegally GREASE TLS12 and TLS13 ServerHello version value
TEST_P(GreaseTestStreamTls123, GreasedServerHelloVersion) {
  EnsureTlsSetup();
  auto filter = MakeTlsFilter<TlsMessageVersionSetter>(
      server_, kTlsHandshakeServerHello, grease_);
  if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
    filter->EnableDecryption();
  }
  ConnectExpectIllegalGreaseFail();
  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
}

// Illegally GREASE TLS12 and TLS13 selected CipherSuite value
TEST_P(GreaseTestStreamTls123, GreasedServerHelloCipherSuite) {
  EnsureTlsSetup();
  auto filter = MakeTlsFilter<SelectedCipherSuiteReplacer>(server_, grease_);
  if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
    filter->EnableDecryption();
  }
  ConnectExpectIllegalGreaseFail();
  client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
}

class GreaseExtensionTestStreamTls13
    : public TlsConnectStreamTls13,
      public ::testing::WithParamInterface<
          std::tuple<uint8_t /* message */, uint16_t /* GREASE */>> {
 public:
  GreaseExtensionTestStreamTls13()
      : TlsConnectStreamTls13(),
        message_(std::get<0>(GetParam())),
        grease_(std::get<1>(GetParam())){};

 protected:
  uint8_t message_;
  uint16_t grease_;
};

// Illegally GREASE TLS13 Server EncryptedExtensions and Certificate Extensions
// NSS currently allows offering unkown extensions in HelloRetryRequests!
TEST_P(GreaseExtensionTestStreamTls13, GreasedServerExtensions) {
  EnsureTlsSetup();
  DataBuffer empty = DataBuffer(1);
  auto filter =
      MakeTlsFilter<TlsExtensionAppender>(server_, message_, grease_, empty);
  filter->EnableDecryption();

  server_->ExpectReceiveAlert(kTlsAlertUnsupportedExtension);
  client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
  ConnectExpectFail();
  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
  server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_EXTENSION_ALERT);
}

// Illegally GREASE TLS12 and TLS13 ServerHello Extensions
TEST_P(GreaseTestStreamTls123, GreasedServerHelloExtensions) {
  EnsureTlsSetup();
  DataBuffer empty = DataBuffer(1);
  auto filter = MakeTlsFilter<TlsExtensionAppender>(
      server_, kTlsHandshakeServerHello, grease_, empty);

  if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
    filter->EnableDecryption();
    server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
  } else {
    server_->ExpectReceiveAlert(kTlsAlertUnsupportedExtension);
  }
  client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
  ConnectExpectFail();
  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
}

// Illegally GREASE TLS13 Client Certificate Extensions
// Server ignores injected client extensions and fails on CertificateVerify
TEST_P(GreaseTestStreamTls13, GreasedClientCertificateExtensions) {
  client_->SetupClientAuth();
  server_->RequestClientAuth(true);
  DataBuffer empty = DataBuffer(1);
  auto filter = MakeTlsFilter<TlsExtensionAppender>(
      client_, kTlsHandshakeCertificate, grease_, empty);
  filter->EnableDecryption();

  server_->ExpectSendAlert(kTlsAlertDecryptError);
  client_->ExpectReceiveAlert(kTlsAlertDecryptError);

  // Manually trigger handshake to avoid race conditions
  StartConnect();
  client_->Handshake();
  server_->Handshake();
  client_->Handshake();
  server_->Handshake();
  client_->Handshake();

  server_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
  client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
}

TEST_F(TlsConnectStreamTls13, GreaseClientHelloExtensionPermutation) {
  EnsureTlsSetup();
  ASSERT_TRUE(SSL_OptionSet(client_->ssl_fd(),
                            SSL_ENABLE_CH_EXTENSION_PERMUTATION,
                            PR_TRUE) == SECSuccess);
  ASSERT_TRUE(SSL_OptionSet(client_->ssl_fd(), SSL_ENABLE_GREASE, PR_TRUE) ==
              SECSuccess);
  Connect();
}

INSTANTIATE_TEST_SUITE_P(GreaseTestTls12, GreaseTestStreamTls12,
                         ::testing::ValuesIn(kTlsGreaseValues));

INSTANTIATE_TEST_SUITE_P(GreaseTestTls13, GreaseTestStreamTls13,
                         ::testing::ValuesIn(kTlsGreaseValues));

INSTANTIATE_TEST_SUITE_P(
    GreaseTestTls123, GreaseTestStreamTls123,
    ::testing::Combine(TlsConnectTestBase::kTlsV12Plus,
                       ::testing::ValuesIn(kTlsGreaseValues)));

INSTANTIATE_TEST_SUITE_P(
    GreaseExtensionTest, GreaseExtensionTestStreamTls13,
    testing::Combine(testing::ValuesIn(kTlsGreaseExtensionMessages),
                     testing::ValuesIn(kTlsGreaseValues)));

}  // namespace nss_test
