// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/component_updater/pki_metadata_component_installer.h"

#include <array>
#include <cstdint>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/base64.h"
#include "base/compiler_specific.h"
#include "base/containers/extend.h"
#include "base/containers/span.h"
#include "base/containers/to_vector.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/string_view_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "base/threading/thread_restrictions.h"
#include "base/types/optional_ref.h"
#include "base/values.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/net/secure_dns_config.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/certificate_transparency/certificate_transparency_config.pb.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/network_service_util.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_throttle_inserter.h"
#include "crypto/hash.h"
#include "crypto/keypair.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/root_store_proto_lite/signer_set.pb.h"
#include "net/cert/test_root_certs.h"
#include "net/cert/x509_certificate.h"
#include "net/dns/dns_test_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/dns/public/util.h"
#include "net/log/net_log_event_type.h"
#include "net/log/test_net_log.h"
#include "net/net_buildflags.h"
#include "net/ssl/ssl_server_config.h"
#include "net/test/cert_test_util.h"
#include "net/test/chrome_root_store_test_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_doh_server.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/strings/str_format.h"

#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
#include "base/test/bind.h"
#include "chrome/browser/ssl/ssl_browsertest_util.h"
#include "net/base/features.h"
#include "net/cert/internal/trust_store_chrome.h"
#include "net/cert/root_store_proto_lite/root_store.pb.h"
#include "net/cert/x509_util.h"
#include "net/test/cert_builder.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
#endif

namespace {

enum class CTEnforcement {
  // Enables CT enforcement.
  kEnabled,
  // Enables CT with one-6962-log CT policy enforcement.
  kEnabledWithOne6962Enforcement,
  // Disables CT enforcement via component updater proto.
  kDisabledByProto,
  // Disables CT enforcement via feature flag.
  kDisabledByFeature
};

int64_t SecondsSinceEpoch(base::Time t) {
  return (t - base::Time::UnixEpoch()).InSeconds();
}

// A CTLog generates a log identity private key, then computes and
// caches several properties from that key that are needed in test cases.
class CTLog {
 public:
  CTLog(std::string_view name,
        base::Time start,
        base::Time end,
        chrome_browser_certificate_transparency::CTLog::LogType type)
      : name_(name), start_(start), end_(end), type_(type) {}

  std::string_view name() const { return name_; }
  base::Time start() const { return start_; }
  base::Time end() const { return end_; }
  chrome_browser_certificate_transparency::CTLog::LogType type() const {
    return type_;
  }

  base::span<const uint8_t> spki() const { return spki_; }
  std::string_view spki_base64() const { return spki_base64_; }

  // Even though the id is just a span of bytes, so this should theoretically
  // return a base::span<const uint8_t> referencing the data we've cached, all the
  // call sites want it as a string.
  std::string id() const { return std::string(base::as_string_view(id_)); }
  std::string_view id_base64() const { return id_base64_; }

  bssl::UniquePtr<EVP_PKEY> key() { return bssl::UpRef(private_key_.key()); }

 private:
  const std::string name_;
  const base::Time start_;
  const base::Time end_;
  const chrome_browser_certificate_transparency::CTLog::LogType type_;

  // The generated private key and things derived from it. Note that the private
  // key itself can't be const, because returning a reference to it in key()
  // above requires mutating its inner refcount.
  crypto::keypair::PrivateKey private_key_{
      crypto::keypair::PrivateKey::GenerateEcP256()};
  const std::vector<uint8_t> spki_{private_key_.ToSubjectPublicKeyInfo()};
  const std::string spki_base64_{base::Base64Encode(spki_)};
  const std::array<uint8_t, crypto::hash::kSha256Size> id_{
      crypto::hash::Sha256(spki_)};
  const std::string id_base64_{base::Base64Encode(id_)};
};

void AddLogToCTConfig(chrome_browser_certificate_transparency::CTConfig* config,
                      const CTLog& log) {
  chrome_browser_certificate_transparency::CTLog* entry =
      config->mutable_log_list()->add_logs();
  entry->set_log_id(log.id_base64());
  entry->set_key(log.spki_base64());
  entry->set_purpose(chrome_browser_certificate_transparency::CTLog::PROD);
  entry->set_log_type(log.type());
  entry->mutable_temporal_interval()->mutable_start()->set_seconds(
      SecondsSinceEpoch(log.start()));
  entry->mutable_temporal_interval()->mutable_end()->set_seconds(
      SecondsSinceEpoch(log.end()));
  chrome_browser_certificate_transparency::CTLog_State* log_state =
      entry->add_state();
  log_state->set_current_state(
      chrome_browser_certificate_transparency::CTLog::USABLE);
  log_state->mutable_state_start()->set_seconds(SecondsSinceEpoch(log.start()));
  chrome_browser_certificate_transparency::CTLog_OperatorChange*
      operator_history = entry->add_operator_history();
  operator_history->set_name(log.name());
  operator_history->mutable_operator_start()->set_seconds(
      SecondsSinceEpoch(log.start()));
}

std::string X509CertificateToString(scoped_refptr<net::X509Certificate> cert) {
  std::vector<std::string> pem_encoded_chain;
  EXPECT_TRUE(cert->GetPEMEncodedChain(&pem_encoded_chain));
  return base::JoinString(pem_encoded_chain, "\n");
}

// Checks that navigation responses were served over a connection where the
// server provided the given `expected_server_certificate_chain`. Note that this
// checks the certificate chain that the server served, not the chain that the
// client built while validating the server's certificate.
class CertificateCheckingThrottle : public content::NavigationThrottle {
 public:
  CertificateCheckingThrottle(
      content::NavigationThrottleRegistry& registry,
      scoped_refptr<net::X509Certificate> expected_server_certificate_chain,
      base::OnceCallback<void(uint8_t)> report_num_responses_callback)
      : content::NavigationThrottle(registry),
        expected_server_certificate_chain_(expected_server_certificate_chain),
        report_num_responses_callback_(
            std::move(report_num_responses_callback)) {}

  CertificateCheckingThrottle(const CertificateCheckingThrottle&) = delete;
  CertificateCheckingThrottle& operator=(const CertificateCheckingThrottle&) =
      delete;
  ~CertificateCheckingThrottle() override {
    std::move(report_num_responses_callback_).Run(num_responses_);
  }

  uint8_t num_responses() const { return num_responses_; }

 protected:
  const char* GetNameForLogging() override {
    return "CertificateCheckingThrottle";
  }

  ThrottleCheckResult WillProcessResponse() override {
    EXPECT_TRUE(navigation_handle()
                    ->GetSSLInfo()
                    ->unverified_cert->EqualsIncludingChain(
                        expected_server_certificate_chain_.get()))
        << "\n\nExpected server chain: "
        << X509CertificateToString(expected_server_certificate_chain_)
        << "\n\nObserved unverified server chain: "
        << X509CertificateToString(
               navigation_handle()->GetSSLInfo()->unverified_cert);
    ++num_responses_;
    return content::NavigationThrottle::PROCEED;
  }

 private:
  scoped_refptr<net::X509Certificate> expected_server_certificate_chain_;
  uint8_t num_responses_ = 0;
  base::OnceCallback<void(uint8_t)> report_num_responses_callback_;
};

class CertificateCheckingThrottleController {
 public:
  void InsertThrottleExpectingCertificate(
      content::WebContents* webcontents,
      scoped_refptr<net::X509Certificate> certificate) {
    num_observed_responses_ = 0;
    throttle_inserter_ =
        std::make_unique<content::TestNavigationThrottleInserter>(
            webcontents,
            base::BindRepeating(
                &CertificateCheckingThrottleController::InsertThrottle,
                base::Unretained(this), certificate));
  }

  void InsertThrottle(
      scoped_refptr<net::X509Certificate> expected_server_certificate,
      content::NavigationThrottleRegistry& registry) {
    registry.AddThrottle(std::make_unique<CertificateCheckingThrottle>(
        registry, expected_server_certificate,
        base::BindOnce(
            &CertificateCheckingThrottleController::UpdateNumObservedResponses,
            base::Unretained(this))));
  }

  size_t num_observed_responses() const { return num_observed_responses_; }

 private:
  void UpdateNumObservedResponses(uint8_t num_responses) {
    num_observed_responses_ += num_responses;
  }

  std::unique_ptr<content::TestNavigationThrottleInserter> throttle_inserter_;
  size_t num_observed_responses_ = 0;
};

// Intended to be bound to the net::SSLServerConfig
// `client_hello_callback_for_testing` to log the list of TAIs the client sent
// to the server.
// TODO(crbug.com/443106392): this callback just adds some debugging info to
// try to investigate a flake, so we could consider removing it when the flake
// is fixed. It's also helpful for understanding if the TAI test setups are
// working correctly though (esp. when adding new tests), so maybe we should
// keep it regardless?
bool LogClientHelloTrustAnchorIDs(const SSL_CLIENT_HELLO* client_hello) {
  const uint8_t* data = nullptr;
  size_t len = 0;
  SSL_early_callback_ctx_extension_get(client_hello, TLSEXT_TYPE_trust_anchors,
                                       &data, &len);

  // SAFETY: SSL_early_callback_ctx_extension_get ensures that `data` has a size
  // of `len`.
  base::span<const uint8_t> UNSAFE_BUFFERS(data_span(data, len));
  LOG(ERROR) << "Trust anchor IDs from Client Hello: "
             << base::HexEncode(data_span);
  return true;
}

std::vector<std::string> GetNetLogCertPemChainsForHost(
    const net::RecordingNetLogObserver& net_log_observer,
    std::string_view hostname) {
  std::vector<std::string> observed_cert_pem;
  for (const auto& entry : net_log_observer.GetEntriesWithType(
           net::NetLogEventType::CERT_VERIFY_PROC)) {
    if (entry.phase != net::NetLogEventPhase::BEGIN) {
      continue;
    }
    const std::string* entry_hostname = entry.params.FindString("host");
    if (!entry_hostname || *entry_hostname != hostname) {
      continue;
    }
    const base::ListValue* cert_pem_list =
        entry.params.FindList("certificates");
    if (!cert_pem_list) {
      continue;
    }
    std::vector<std::string> chain_pems;
    for (const base::Value& value : *cert_pem_list) {
      if (!value.is_string()) {
        continue;
      }
      chain_pems.push_back(value.GetString());
    }
    observed_cert_pem.push_back(base::JoinString(chain_pems, "\n"));
  }
  return observed_cert_pem;
}

}  // namespace

namespace component_updater {

// TODO(crbug.com/341136041): add tests for pinning enforcement.
class PKIMetadataComponentUpdaterTest
    : public InProcessBrowserTest,
      public testing::WithParamInterface<CTEnforcement>,
      public PKIMetadataComponentInstallerService::Observer {
 public:
  PKIMetadataComponentUpdaterTest() {
    switch (GetParam()) {
      case CTEnforcement::kEnabled:
        scoped_feature_list_.InitWithFeatures(
            /*enabled_features=*/
            {features::kCertificateTransparencyAskBeforeEnabling},
            /*disabled_features=*/{net::features::kEnforceOneRfc6962CtPolicy});
        break;

      case CTEnforcement::kEnabledWithOne6962Enforcement:
        scoped_feature_list_.InitWithFeatures(
            /*enabled_features=*/{features::
                                      kCertificateTransparencyAskBeforeEnabling,
                                  net::features::kEnforceOneRfc6962CtPolicy},
            /*disabled_features=*/{});
        break;

      case CTEnforcement::kDisabledByProto:
        scoped_feature_list_.InitAndEnableFeature(
            features::kCertificateTransparencyAskBeforeEnabling);
        break;

      case CTEnforcement::kDisabledByFeature:
        scoped_feature_list_.InitAndDisableFeature(
            features::kCertificateTransparencyAskBeforeEnabling);
        break;
    }
  }

  void SetUpInProcessBrowserTestFixture() override {
    PKIMetadataComponentInstallerService::GetInstance()->AddObserver(this);
    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
    ASSERT_TRUE(component_dir_.CreateUniqueTempDir());
    host_resolver()->AddRule("*", "127.0.0.1");

    // Set up a configuration that will enable or disable CT enforcement
    // depending on the test parameter.
    chrome_browser_certificate_transparency::CTConfig ct_config;
    ct_config.set_disable_ct_enforcement(GetParam() ==
                                         CTEnforcement::kDisabledByProto);
    ct_config.mutable_log_list()->mutable_timestamp()->set_seconds(
        SecondsSinceEpoch(base::Time::Now()));
    ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance()
                    ->WriteCTDataForTesting(component_dir_.GetPath(),
                                            ct_config.SerializeAsString()));
  }

  void TearDownInProcessBrowserTestFixture() override {
    PKIMetadataComponentInstallerService::GetInstance()->RemoveObserver(this);
  }

  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();
    // Wait for configuration set in `SetUpInProcessBrowserTestFixture` to load.
    WaitForPKIConfiguration(1);
  }

 protected:
  // Waits for the PKI to have been configured at least |expected_times|.
  void WaitForPKIConfiguration(int expected_times) {
    if (GetParam() == CTEnforcement::kDisabledByFeature) {
      // When CT is disabled by the feature flag there are no callbacks to
      // wait on, so just spin the runloop.
      base::RunLoop().RunUntilIdle();
      EXPECT_EQ(pki_metadata_configured_times_, 0);
    } else {
      expected_pki_metadata_configured_times_ = expected_times;
      if (pki_metadata_configured_times_ >=
          expected_pki_metadata_configured_times_) {
        return;
      }
      base::RunLoop run_loop;
      pki_metadata_config_closure_ = run_loop.QuitClosure();
      run_loop.Run();
    }
  }

  const base::FilePath& GetComponentDirPath() const {
    return component_dir_.GetPath();
  }

  bool is_ct_enforced() const {
    return GetParam() == CTEnforcement::kEnabled ||
           GetParam() == CTEnforcement::kEnabledWithOne6962Enforcement;
  }

  void DoTestAtLeastOneRFC6962LogPolicy(
      chrome_browser_certificate_transparency::CTLog::LogType log_type,
      bool expect_ct_error);

 private:
  void OnCTLogListConfigured() override {
    ++pki_metadata_configured_times_;
    if (pki_metadata_config_closure_ &&
        pki_metadata_configured_times_ >=
            expected_pki_metadata_configured_times_) {
      std::move(pki_metadata_config_closure_).Run();
    }
  }

  base::test::ScopedFeatureList scoped_feature_list_;
  base::ScopedTempDir component_dir_;

  base::OnceClosure pki_metadata_config_closure_;
  int expected_pki_metadata_configured_times_ = 0;
  int pki_metadata_configured_times_ = 0;
};

// Tests that the PKI Metadata configuration is recovered after a network
// service restart.
IN_PROC_BROWSER_TEST_P(PKIMetadataComponentUpdaterTest,
                       ReloadsPKIMetadataConfigAfterCrash) {
  // Network service is not running out of process, so cannot be crashed.
  if (!content::IsOutOfProcessNetworkService()) {
    return;
  }

  // Make the test root be interpreted as a known root so that CT will be
  // required.
  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  net::ScopedTestKnownRoot scoped_known_root(root_cert.get());

  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  static constexpr char kHostname[] = "example.com";
  https_server_ok.SetCertHostnames({kHostname});
  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(https_server_ok.Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL(kHostname, "/simple.html")));

  // Check that the page is blocked depending on CT enforcement.
  content::WebContents* tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  if (is_ct_enforced()) {
    EXPECT_NE(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  } else {
    EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  }

  // Restart the network service.
  SimulateNetworkServiceCrash();
  // Wait for the restarted network service to load the component update data
  // that is already on disk.
  WaitForPKIConfiguration(2);

  // Check that the page is still blocked depending on CT enforcement.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL(kHostname, "/simple.html")));
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  if (is_ct_enforced()) {
    EXPECT_NE(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  } else {
    EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  }
}

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentUpdaterTest, TestCTUpdate) {
  const base::Time kLogStart = base::Time::Now() - base::Days(1);
  const base::Time kLogEnd = base::Time::Now() + base::Days(1);

  CTLog log1("log operator 1", kLogStart, kLogEnd,
             chrome_browser_certificate_transparency::CTLog::RFC6962);
  CTLog log2(
      "log operator 2", kLogStart, kLogEnd,
      chrome_browser_certificate_transparency::CTLog::LOG_TYPE_UNSPECIFIED);

  // Make the test root be interpreted as a known root so that CT will be
  // required.
  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  net::ScopedTestKnownRoot scoped_known_root(root_cert.get());

  // Start a test server that uses a certificate with SCTs for the above test
  // logs.
  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig server_config;
  // The same hostname is used for each request, which verifies that the CT log
  // updates cause verifier caches and socket pool invalidation, so that the
  // next request for the same host will use the updated CT state.
  server_config.dns_names = {"example.com"};
  server_config.embedded_scts.emplace_back(log1.id(), log1.key(),
                                           base::Time::Now());
  server_config.embedded_scts.emplace_back(log2.id(), log2.key(),
                                           base::Time::Now());
  https_server_ok.SetSSLConfig(server_config);

  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(https_server_ok.Start());

  // Check that the page is blocked depending on CT enforcement.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("example.com", "/simple.html")));
  content::WebContents* tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  if (is_ct_enforced()) {
    EXPECT_NE(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  } else {
    EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  }

  // Update with a CT configuration that trusts log1 and log2
  //
  // Set up a configuration that will enable or disable CT enforcement
  // depending on the test parameter.
  chrome_browser_certificate_transparency::CTConfig ct_config;
  ct_config.set_disable_ct_enforcement(GetParam() ==
                                       CTEnforcement::kDisabledByProto);
  ct_config.mutable_log_list()->mutable_timestamp()->set_seconds(
      SecondsSinceEpoch(base::Time::Now()));
  AddLogToCTConfig(&ct_config, log1);
  AddLogToCTConfig(&ct_config, log2);

  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance()
                    ->WriteCTDataForTesting(GetComponentDirPath(),
                                            ct_config.SerializeAsString()));
  }

  // Should be trusted now.
  PKIMetadataComponentInstallerService::GetInstance()
      ->ReconfigureAfterNetworkRestart();
  WaitForPKIConfiguration(2);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("example.com", "/simple.html")));
  EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());

  // Update CT configuration again with the same CT logs but mark the 1st log
  // as retired.
  {
    chrome_browser_certificate_transparency::CTLog* log =
        ct_config.mutable_log_list()->mutable_logs(0);
    log->clear_state();
    // Log states are in reverse chronological order, so the most recent state
    // comes first.
    {
      chrome_browser_certificate_transparency::CTLog_State* log_state =
          log->add_state();
      log_state->set_current_state(
          chrome_browser_certificate_transparency::CTLog::RETIRED);
      log_state->mutable_state_start()->set_seconds(
          SecondsSinceEpoch(kLogStart) + 1);
    }
    {
      chrome_browser_certificate_transparency::CTLog_State* log_state =
          log->add_state();
      log_state->set_current_state(
          chrome_browser_certificate_transparency::CTLog::USABLE);
      log_state->mutable_state_start()->set_seconds(
          SecondsSinceEpoch(kLogStart));
    }
  }
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance()
                    ->WriteCTDataForTesting(GetComponentDirPath(),
                                            ct_config.SerializeAsString()));
  }

  // Should be untrusted again since 2 logs are required for diversity. Both
  // SCTs should verify successfully but only one of them is accepted as the
  // other has a timestamp after the log retirement state change timestamp.
  PKIMetadataComponentInstallerService::GetInstance()
      ->ReconfigureAfterNetworkRestart();
  WaitForPKIConfiguration(3);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("example.com", "/simple.html")));
  if (is_ct_enforced()) {
    EXPECT_NE(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  } else {
    EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  }
}

// Tests that at least one RFC6962 log policy is correctly applied when Static
// CT API enforcement is enabled. All logs in the test will be set to
// `log_type`. If `expect_ct_error_with_one_6962_policy_enforcement` is true,
// CT checks with Static CT API enforcement should cause an SSL error.
void PKIMetadataComponentUpdaterTest::DoTestAtLeastOneRFC6962LogPolicy(
    chrome_browser_certificate_transparency::CTLog::LogType log_type,
    bool expect_ct_error_with_one_6962_policy_enforcement) {
  const base::Time kLogStart = base::Time::Now() - base::Days(1);
  const base::Time kLogEnd = base::Time::Now() + base::Days(1);
  CTLog log1("log operator 1", kLogStart, kLogEnd, log_type);
  CTLog log2("log operator 2", kLogStart, kLogEnd, log_type);

  // Make the test root be interpreted as a known root so that CT will be
  // required.
  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  net::ScopedTestKnownRoot scoped_known_root(root_cert.get());

  // Start a test server that uses a certificate with SCTs for the above test
  // logs.
  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig server_config;
  // The same hostname is used for each request, which verifies that the CT log
  // updates cause verifier caches and socket pool invalidation, so that the
  // next request for the same host will use the updated CT state.
  server_config.dns_names = {"example.com"};
  server_config.embedded_scts.emplace_back(log1.id(), log1.key(),
                                           base::Time::Now());
  server_config.embedded_scts.emplace_back(log2.id(), log2.key(),
                                           base::Time::Now());
  https_server_ok.SetSSLConfig(server_config);

  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(https_server_ok.Start());

  // Check that the page is blocked depending on CT enforcement.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("example.com", "/simple.html")));
  content::WebContents* tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  if (is_ct_enforced()) {
    EXPECT_NE(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  } else {
    EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  }

  // Update with a CT configuration that trusts log1 and log2. Neither of
  // these logs is RFC6962, so the SCTs will not pass validation.
  //
  // Set up a configuration that will enable or disable CT enforcement
  // depending on the test parameter.
  chrome_browser_certificate_transparency::CTConfig ct_config;
  ct_config.set_disable_ct_enforcement(GetParam() ==
                                       CTEnforcement::kDisabledByProto);
  ct_config.mutable_log_list()->mutable_timestamp()->set_seconds(
      SecondsSinceEpoch(base::Time::Now()));
  AddLogToCTConfig(&ct_config, log1);
  AddLogToCTConfig(&ct_config, log2);

  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance()
                    ->WriteCTDataForTesting(GetComponentDirPath(),
                                            ct_config.SerializeAsString()));
  }

  PKIMetadataComponentInstallerService::GetInstance()
      ->ReconfigureAfterNetworkRestart();
  WaitForPKIConfiguration(2);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("example.com", "/simple.html")));

  if (is_ct_enforced()) {
    if (expect_ct_error_with_one_6962_policy_enforcement) {
      EXPECT_NE(u"OK",
                chrome_test_utils::GetActiveWebContents(this)->GetTitle());
    } else {
      EXPECT_EQ(u"OK",
                chrome_test_utils::GetActiveWebContents(this)->GetTitle());
    }
  } else {
    EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
  }
}

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentUpdaterTest,
                       TestAtLeastOneRFC6962LogPolicy_StaticCTAPILogs) {
  // Test with all logs with Static CT API type. Since at least one RFC6962 log
  // is expected when the one-6962 policy is enabled, this should show an error.
  DoTestAtLeastOneRFC6962LogPolicy(
      chrome_browser_certificate_transparency::CTLog::STATIC_CT_API,
      /*expect_ct_error_with_one_6962_policy_enforcement=*/true);
}

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentUpdaterTest,
                       TestAtLeastOneRFC6962LogPolicy_UnspecifiedLogTypes) {
  // Test with all logs with unspecified type. These are treated as RFC6962
  // logs so they shouldn't cause an SSL error.
  // TODO(crbug.com/370724580): Disallow unspecified log type once all logs in
  // the hardcoded and component updater protos have proper log types.
  DoTestAtLeastOneRFC6962LogPolicy(
      chrome_browser_certificate_transparency::CTLog::LOG_TYPE_UNSPECIFIED,
      /*expect_ct_error_with_one_6962_policy_enforcement=*/false);
}

INSTANTIATE_TEST_SUITE_P(
    PKIMetadataComponentUpdater,
    PKIMetadataComponentUpdaterTest,
    testing::Values(CTEnforcement::kEnabled,
                    CTEnforcement::kEnabledWithOne6962Enforcement,
                    CTEnforcement::kDisabledByProto,
                    CTEnforcement::kDisabledByFeature));

#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)

class PKIMetadataComponentChromeRootStoreUpdateTest
    : public InProcessBrowserTest,
      public PKIMetadataComponentInstallerService::Observer {
 public:
  void SetUpInProcessBrowserTestFixture() override {
    SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting(
        false);
    PKIMetadataComponentInstallerService::GetInstance()->AddObserver(this);
    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
    ASSERT_TRUE(component_dir_.CreateUniqueTempDir());
    host_resolver()->AddRule("*", "127.0.0.1");
  }

  void TearDownInProcessBrowserTestFixture() override {
    PKIMetadataComponentInstallerService::GetInstance()->RemoveObserver(this);
    SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting(
        std::nullopt);
  }

  class CRSWaiter {
   public:
    explicit CRSWaiter(PKIMetadataComponentChromeRootStoreUpdateTest* test) {
      test_ = test;
      test_->crs_config_closure_ = run_loop_.QuitClosure();
    }
    void Wait() { run_loop_.Run(); }

   private:
    base::RunLoop run_loop_;
    raw_ptr<PKIMetadataComponentChromeRootStoreUpdateTest> test_;
  };

  class MtcMetadataWaiter {
   public:
    explicit MtcMetadataWaiter(
        PKIMetadataComponentChromeRootStoreUpdateTest* test) {
      test_ = test;
      test_->mtc_metadata_config_closure_ = run_loop_.QuitClosure();
    }
    void Wait() { run_loop_.Run(); }

   private:
    base::RunLoop run_loop_;
    raw_ptr<PKIMetadataComponentChromeRootStoreUpdateTest> test_;
  };

  void InstallCRSUpdate(const chrome_root_store::RootStore& root_store_proto,
                        base::optional_ref<const chrome_root_store::MtcConfig>
                            mtc_config = std::nullopt) {
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(
          PKIMetadataComponentInstallerService::GetInstance()
              ->WriteCRSDataForTesting(component_dir_.GetPath(),
                                       root_store_proto.SerializeAsString()));
      if (mtc_config) {
        ASSERT_TRUE(
            PKIMetadataComponentInstallerService::GetInstance()
                ->WriteSignerSetDataForTesting(
                    component_dir_.GetPath(), mtc_config->SerializeAsString()));
      }
    }

    CRSWaiter waiter(this);
    PKIMetadataComponentInstallerService::GetInstance()
        ->ConfigureChromeRootStore();
    waiter.Wait();
  }

  void InstallCRSUpdate(const std::vector<std::string>& der_roots) {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++last_used_crs_version_);
    for (const auto& der_root : der_roots) {
      root_store_proto.add_trust_anchors()->set_der(der_root);
    }

    InstallCRSUpdate(root_store_proto);
  }

  void InstallMtcMetadataUpdate(
      const chrome_root_store::MtcMetadata& mtc_metadata_proto) {
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance()
                      ->WriteMtcMetadataForTesting(
                          component_dir_.GetPath(),
                          mtc_metadata_proto.SerializeAsString()));
    }

    MtcMetadataWaiter waiter(this);
    PKIMetadataComponentInstallerService::GetInstance()->ConfigureMtcMetadata();
    waiter.Wait();
  }

 protected:
  base::ScopedTempDir component_dir_;

 private:
  void OnChromeRootStoreConfigured() override {
    if (crs_config_closure_) {
      std::move(crs_config_closure_).Run();
    }
  }

  void OnMtcMetadataConfigured() override {
    if (mtc_metadata_config_closure_) {
      std::move(mtc_metadata_config_closure_).Run();
    }
  }

  base::OnceClosure crs_config_closure_;
  base::OnceClosure mtc_metadata_config_closure_;
  int64_t last_used_crs_version_ = net::CompiledChromeRootStoreVersion();
};

IN_PROC_BROWSER_TEST_F(PKIMetadataComponentChromeRootStoreUpdateTest,
                       CheckCRSUpdate) {
  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig server_config;
  server_config.dns_names = {"*.example.com"};
  https_server_ok.SetSSLConfig(server_config);
  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");

  // Clear test roots so that cert validation only happens with
  // what's in Chrome Root Store.
  net::TestRootCerts::GetInstance()->Clear();

  ASSERT_TRUE(https_server_ok.Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("a.example.com", "/simple.html")));

  // Check that the page is blocked depending on contents of Chrome Root Store.
  content::WebContents* tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticationBrokenState(
      tab, net::CERT_STATUS_AUTHORITY_INVALID,
      ssl_test_util::AuthState::SHOWING_INTERSTITIAL);

  {
    scoped_refptr<net::X509Certificate> root_cert =
        net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
    ASSERT_TRUE(root_cert);
    InstallCRSUpdate({std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()))});
  }

  base::HistogramTester histograms;
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("b.example.com", "/simple.html")));

  // Check that the page is allowed due to contents of Chrome Root Store.
  tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE);

  // The proto did not have a crs_root_id set on the anchor, check that the
  // histograms recorded the unknown bucket.
  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  EXPECT_GE(
      histograms.GetBucketCount("Net.Certificate.TrustAnchor2.Request",
                                net::CertVerifyResult::kCrsRootIdUnknownId),
      1u);
  histograms.ExpectUniqueSample("Net.Certificate.TrustAnchor2.Verify",
                                net::CertVerifyResult::kCrsRootIdUnknownId, 1u);

  {
    // We reject empty CRS updates, so create a new cert root that doesn't match
    // what the test server uses.
    auto [leaf, root] = net::CertBuilder::CreateSimpleChain2();
    InstallCRSUpdate({root->GetDER()});
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("c.example.com", "/simple.html")));

  // Check that the page is blocked depending on contents of Chrome Root Store.
  tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticationBrokenState(
      tab, net::CERT_STATUS_AUTHORITY_INVALID,
      ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
}

// Similar to CheckCRSUpdate, except using the same hostname for all requests.
// This tests whether the CRS update causes cached verification results to be
// disregarded.
IN_PROC_BROWSER_TEST_F(PKIMetadataComponentChromeRootStoreUpdateTest,
                       CheckCRSUpdateAffectsCachedVerifications) {
  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig server_config;
  server_config.dns_names = {"*.example.com"};
  https_server_ok.SetSSLConfig(server_config);
  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");

  // Clear test roots so that cert validation only happens with
  // what's in Chrome Root Store.
  net::TestRootCerts::GetInstance()->Clear();

  static constexpr char kHostname[] = "a.example.com";

  ASSERT_TRUE(https_server_ok.Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL(kHostname, "/simple.html")));

  // Check that the page is blocked depending on contents of Chrome Root Store.
  content::WebContents* tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticationBrokenState(
      tab, net::CERT_STATUS_AUTHORITY_INVALID,
      ssl_test_util::AuthState::SHOWING_INTERSTITIAL);

  {
    scoped_refptr<net::X509Certificate> root_cert =
        net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
    ASSERT_TRUE(root_cert);
    InstallCRSUpdate({std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()))});
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL(kHostname, "/title2.html")));

  // Check that the page is allowed due to contents of Chrome Root Store.
  tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
            u"Title Of Awesomeness");
  ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE);

  {
    // We reject empty CRS updates, so create a new cert root that doesn't match
    // what the test server uses.
    auto [leaf, root] = net::CertBuilder::CreateSimpleChain2();
    InstallCRSUpdate({root->GetDER()});
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL(kHostname, "/title3.html")));

  // Check that the page is blocked depending on contents of Chrome Root Store.
  tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
            u"Title Of Awesomeness");
  EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
            u"Title Of More Awesomeness");
  ssl_test_util::CheckAuthenticationBrokenState(
      tab, net::CERT_STATUS_AUTHORITY_INVALID,
      ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
}

IN_PROC_BROWSER_TEST_F(PKIMetadataComponentChromeRootStoreUpdateTest,
                       CrsRootId) {
  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig server_config;
  server_config.dns_names = {"*.example.com"};
  https_server_ok.SetSSLConfig(server_config);
  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");

  // Clear test roots so that cert validation only happens with
  // what's in Chrome Root Store.
  net::TestRootCerts::GetInstance()->Clear();

  ASSERT_TRUE(https_server_ok.Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("a.example.com", "/simple.html")));

  // Check that the page is blocked depending on contents of Chrome Root Store.
  content::WebContents* tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticationBrokenState(
      tab, net::CERT_STATUS_AUTHORITY_INVALID,
      ssl_test_util::AuthState::SHOWING_INTERSTITIAL);

  int64_t crs_version = net::CompiledChromeRootStoreVersion();
  constexpr int32_t kFakeCrsRootId = 98238;

  {
    scoped_refptr<net::X509Certificate> root_cert =
        net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
    ASSERT_TRUE(root_cert);
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->set_crs_root_id(kFakeCrsRootId);
    InstallCRSUpdate(root_store_proto);
  }

  base::HistogramTester histograms;
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("b.example.com", "/simple.html")));

  // Check that the anchor histograms are recorded using the id from the
  // Chrome Root Store proto.
  tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE);

  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
  EXPECT_GE(histograms.GetBucketCount("Net.Certificate.TrustAnchor2.Request",
                                      kFakeCrsRootId),
            1u);
  histograms.ExpectUniqueSample("Net.Certificate.TrustAnchor2.Verify",
                                kFakeCrsRootId, 1u);
}

IN_PROC_BROWSER_TEST_F(PKIMetadataComponentChromeRootStoreUpdateTest,
                       UpdateTrustAnchorIDs) {
  content::StoragePartition* partition =
      chrome_test_utils::GetActiveWebContents(this)
          ->GetBrowserContext()
          ->GetDefaultStoragePartition();
  int64_t crs_version = net::CompiledChromeRootStoreVersion();
  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  scoped_refptr<net::X509Certificate> intermediate1 = net::ImportCertFromFile(
      net::GetTestCertsDirectory(), "intermediate_ca_cert.pem");
  ASSERT_TRUE(intermediate1);
  scoped_refptr<net::X509Certificate> intermediate2 = net::ImportCertFromFile(
      net::GetTestCertsDirectory(), "verisign_intermediate_ca_2016.pem");
  ASSERT_TRUE(intermediate2);

  // Test that the initial set of Trust Anchor IDs comes from the compiled-in
  // root store.
  {
    std::vector<std::vector<uint8_t>> expected_trust_anchor_ids =
        net::TrustStoreChrome::GetTrustAnchorIDsFromCompiledInRootStore();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAreArray(expected_trust_anchor_ids));
  }

  // Install CRS update that contains no trusted Trust Anchor IDs.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    InstallCRSUpdate(root_store_proto);
    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    EXPECT_TRUE(future.Get().empty());
  }

  // Install CRS update that contains two trusted Trust Anchor IDs.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->set_trust_anchor_id({0x01, 0x02, 0x03});

    chrome_root_store::TrustAnchor* additional_cert1 =
        root_store_proto.add_additional_certs();
    additional_cert1->set_der(
        std::string(net::x509_util::CryptoBufferAsStringPiece(
            intermediate1->cert_buffer())));
    additional_cert1->set_trust_anchor_id({0x01, 0x02});
    // `additional_cert1`'s trust anchor ID should be ignored because it is not
    // configured as a TLS trust anchor.
    additional_cert1->set_tls_trust_anchor(false);

    chrome_root_store::TrustAnchor* additional_cert2 =
        root_store_proto.add_additional_certs();
    additional_cert2->set_der(
        std::string(net::x509_util::CryptoBufferAsStringPiece(
            intermediate2->cert_buffer())));
    additional_cert2->set_trust_anchor_id({0x02, 0x03});
    additional_cert2->set_tls_trust_anchor(true);

    InstallCRSUpdate(root_store_proto);

    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();

    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    EXPECT_THAT(future.Get(), testing::UnorderedElementsAre(
                                  std::vector<uint8_t>({0x01, 0x02, 0x3}),
                                  std::vector<uint8_t>({0x02, 0x03})));
  }
}

// Tests that when new network contexts are created after a Trust Anchor IDs
// component update is received, the new network context uses the Trust Anchor
// IDs from the component updater.
IN_PROC_BROWSER_TEST_F(PKIMetadataComponentChromeRootStoreUpdateTest,
                       NewNetworkContextAfterUpdatingTrustAnchorIDs) {
  // This test is only works with an out-of-process network service because it
  // uses a network service crash/restart to test what happens when a new
  // network context is created.
  if (content::IsInProcessNetworkService()) {
    return;
  }

  content::StoragePartition* partition =
      chrome_test_utils::GetActiveWebContents(this)
          ->GetBrowserContext()
          ->GetDefaultStoragePartition();
  int64_t crs_version = net::CompiledChromeRootStoreVersion();
  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);

  // Install CRS update that contains one trusted Trust Anchor IDs.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->set_trust_anchor_id(
        {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08});
    InstallCRSUpdate(root_store_proto);
    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAre(std::vector<uint8_t>(
                    {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08})));
  }

  // Simulate a network service crash and restart, and check that the newly
  // created network service uses the Trust Anchor ID from the prior component
  // update.
  SimulateNetworkServiceCrash();
  // Flush the interface to make sure it notices the crash.
  partition->FlushNetworkInterfaceForTesting();
  {
    // Just to be sure that the test is testing what it intends to, check that a
    // network context has been created.
    // TODO(crbug.org/478890190): We probably need to add an identifier to
    // NetworkContext to verify that "new" network context is created.
    ASSERT_NE(nullptr, partition->GetNetworkContext());

    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAre(std::vector<uint8_t>(
                    {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08})));
  }
}

IN_PROC_BROWSER_TEST_F(PKIMetadataComponentChromeRootStoreUpdateTest,
                       CheckCRSUpdateDnsConstraint) {
  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig server_config;
  server_config.dns_names = {"*.example.com"};
  https_server_ok.SetSSLConfig(server_config);
  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");

  // Clear test roots so that cert validation only happens with
  // what's in Chrome Root Store.
  net::TestRootCerts::GetInstance()->Clear();

  ASSERT_TRUE(https_server_ok.Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("a.example.com", "/simple.html")));

  // The page should be blocked as the test root is not trusted yet.
  content::WebContents* tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticationBrokenState(
      tab, net::CERT_STATUS_AUTHORITY_INVALID,
      ssl_test_util::AuthState::SHOWING_INTERSTITIAL);

  int64_t crs_version = net::CompiledChromeRootStoreVersion();
  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  // Install CRS update that trusts root with a constraint that matches the
  // leaf's subjectAltName.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->add_constraints()->add_permitted_dns_names("example.com");

    InstallCRSUpdate(root_store_proto);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("b.example.com", "/simple.html")));

  // Check that the page is allowed now.
  tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE);

  // Install CRS update that trusts root with a constraint that does not match
  // the leaf's subjectAltName.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->add_constraints()->add_permitted_dns_names("example.org");

    InstallCRSUpdate(root_store_proto);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("c.example.com", "/simple.html")));

  // Check that the page is blocked now.
  tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticationBrokenState(
      tab, net::CERT_STATUS_AUTHORITY_INVALID,
      ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
}

class PKIMetadataComponentChromeRootStoreMtcMetadataTest
    : public PKIMetadataComponentChromeRootStoreUpdateTest,
      public testing::WithParamInterface<std::tuple<bool, bool, bool>> {
 public:
  PKIMetadataComponentChromeRootStoreMtcMetadataTest() {
    feature_list_.InitWithFeatureStates(
        {{net::features::kVerifyMTCs, mtcs_enabled()},
         {net::features::kTestRootStore, test_roots_enabled()},
         {net::features::kTLSTrustAnchorIDs, true},
         {net::features::kNonMtcTrustAnchorIDs, true}});
  }

  bool mtcs_enabled() const { return std::get<0>(GetParam()); }
  bool test_roots_enabled() const { return std::get<1>(GetParam()); }
  bool use_test_realm() const { return std::get<2>(GetParam()); }

  bool expect_test_mtc_is_used() const {
    return mtcs_enabled() && (!use_test_realm() || test_roots_enabled());
  }

  chrome_root_store::Realm realm() const {
    return use_test_realm() ? chrome_root_store::REALM_UNTRUSTED_VALIDATION_ONLY
                            : chrome_root_store::REALM_PUBLICLY_TRUSTED;
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

INSTANTIATE_TEST_SUITE_P(
    ,
    PKIMetadataComponentChromeRootStoreMtcMetadataTest,
    testing::Combine(testing::Bool(), testing::Bool(), testing::Bool()),
    [](const testing::TestParamInfo<
        PKIMetadataComponentChromeRootStoreMtcMetadataTest::ParamType>& info) {
      return base::StrCat(
          {std::get<0>(info.param) ? "MtcsOn" : "MtcsOff",
           std::get<1>(info.param) ? "TestRootsOn" : "TestRootsOff",
           std::get<2>(info.param) ? "UseTestRealm" : "UsePublicRealm"});
    });

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentChromeRootStoreMtcMetadataTest,
                       TrustAnchorIDsWhenUpdateMtcMetadataBeforeCRS) {
  content::StoragePartition* partition =
      chrome_test_utils::GetActiveWebContents(this)
          ->GetBrowserContext()
          ->GetDefaultStoragePartition();
  int64_t crs_version = net::CompiledChromeRootStoreVersion();
  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  scoped_refptr<net::X509Certificate> intermediate1 = net::ImportCertFromFile(
      net::GetTestCertsDirectory(), "intermediate_ca_cert.pem");
  ASSERT_TRUE(intermediate1);
  scoped_refptr<net::X509Certificate> intermediate2 = net::ImportCertFromFile(
      net::GetTestCertsDirectory(), "verisign_intermediate_ca_2016.pem");
  ASSERT_TRUE(intermediate2);

  // Test that the initial set of Trust Anchor IDs comes from the compiled-in
  // root store.
  {
    std::vector<std::vector<uint8_t>> expected_trust_anchor_ids =
        net::TrustStoreChrome::GetTrustAnchorIDsFromCompiledInRootStore();
    if (mtcs_enabled()) {
      base::Extend(
          expected_trust_anchor_ids,
          net::TrustStoreChrome::GetTrustedMtcCaIDsFromCompiledInRootStore());
    }
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAreArray(expected_trust_anchor_ids));
  }

  static constexpr uint8_t kMtcCaWithLandmarksId[] = {0x01, 0x02, 0x03};
  static constexpr uint8_t kMtcCaInMetadataWithNoLandmarksId[] = {0x01, 0x02,
                                                                  0x04};
  static constexpr uint8_t kMtcCaStandaloneId[] = {0x01, 0x02, 0x05};
  // Install MTC metadata update that contains trusted landmark data for MTCs.
  // Before we've loaded a CRS update proto, these should be used if they match
  // the base_ids of the compiled-in trusted MTC issuers. The TAIs for the
  // compiled-in classic trust anchors should also still be present.
  {
    chrome_root_store::MtcMetadata mtc_metadata_proto;
    mtc_metadata_proto.set_update_time_seconds(
        SecondsSinceEpoch(base::Time::Now()));

    // MTC anchor metadata matching the fake MTC anchors that will be loaded in
    // the CRS update proto in the next part of the test.
    {
      chrome_root_store::MtcAnchorData* mtc_anchor_metadata =
          mtc_metadata_proto.add_mtc_anchor_data();
      mtc_anchor_metadata->set_ca_id(
          base::as_string_view(kMtcCaWithLandmarksId));
      chrome_root_store::MtcLogData* log_data =
          mtc_anchor_metadata->add_mtc_log_data();
      log_data->set_log_number(2);
      log_data->mutable_trusted_landmark_ids_range()
          ->set_min_active_landmark_inclusive(3);
      log_data->mutable_trusted_landmark_ids_range()
          ->set_last_landmark_inclusive(5);
      auto* subtree = log_data->add_trusted_subtrees();
      subtree->set_start_inclusive(0);
      subtree->set_end_exclusive(1);
      subtree->set_hash(std::string(32, 'a'));
    }
    {
      chrome_root_store::MtcAnchorData* mtc_anchor_metadata =
          mtc_metadata_proto.add_mtc_anchor_data();
      mtc_anchor_metadata->set_ca_id(
          base::as_string_view(kMtcCaInMetadataWithNoLandmarksId));
      chrome_root_store::MtcIndexRange* revoked_range =
          mtc_anchor_metadata->add_revoked_indices();
      revoked_range->set_start_inclusive(5);
      revoked_range->set_end_exclusive(10);
    }

    // MTC anchor metadata matching a compiled-in MTC anchor.
    auto expected_builtin_trusted_mtc_ca_ids =
        net::TrustStoreChrome::GetTrustedMtcCaIDsFromCompiledInRootStore();
    if (!expected_builtin_trusted_mtc_ca_ids.empty()) {
      std::vector<uint8_t> ca_id = expected_builtin_trusted_mtc_ca_ids.back();
      chrome_root_store::MtcAnchorData* mtc_anchor_metadata =
          mtc_metadata_proto.add_mtc_anchor_data();
      mtc_anchor_metadata->set_ca_id(base::as_string_view(ca_id));
      chrome_root_store::MtcLogData* log_data =
          mtc_anchor_metadata->add_mtc_log_data();
      log_data->set_log_number(3);
      log_data->mutable_trusted_landmark_ids_range()
          ->set_min_active_landmark_inclusive(1);
      log_data->mutable_trusted_landmark_ids_range()
          ->set_last_landmark_inclusive(2);
      auto* subtree = log_data->add_trusted_subtrees();
      subtree->set_start_inclusive(0);
      subtree->set_end_exclusive(1);
      subtree->set_hash(std::string(32, 'a'));
      // Replace this CA id in the list of expected IDs, since this CA will be
      // advertised using the landmark group ID instead.
      expected_builtin_trusted_mtc_ca_ids.back() =
          net::x509_util::CreateMtcLandmarkGroupTrustAnchorID(ca_id, 3, 2);
    }

    InstallMtcMetadataUpdate(mtc_metadata_proto);
    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    // Test that the set of Trust Anchor IDs is the compiled-in ones plus the
    // one matching the builtin MTC Anchor that we added a matching metadata.
    std::vector<std::vector<uint8_t>> expected_trust_anchor_ids =
        net::TrustStoreChrome::GetTrustAnchorIDsFromCompiledInRootStore();
    if (mtcs_enabled() && !expected_builtin_trusted_mtc_ca_ids.empty()) {
      base::Extend(expected_trust_anchor_ids,
                   expected_builtin_trusted_mtc_ca_ids);
    }
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAreArray(expected_trust_anchor_ids));
  }

  // Install CRS update that contains trusted MTC Issuers matching the added
  // MtcMetadata, as well as a traditional anchor with a TAI.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);

    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->set_trust_anchor_id({0x05, 0x06, 0x07});

    chrome_root_store::MtcConfig mtc_config;
    mtc_config.mutable_signer_set()->mutable_timestamp()->set_seconds(
        base::Time::Now().InSecondsFSinceUnixEpoch());
    net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                            kMtcCaStandaloneId, "op1", std::nullopt)
        ->set_realm(realm());
    net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                            kMtcCaWithLandmarksId, "op2", std::nullopt)
        ->set_realm(realm());
    net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                            kMtcCaInMetadataWithNoLandmarksId, "op3",
                            std::nullopt)
        ->set_realm(realm());

    InstallCRSUpdate(root_store_proto, mtc_config);

    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());

    std::vector<std::vector<uint8_t>> expected_trust_anchor_ids = {
        std::vector<uint8_t>({0x05, 0x06, 0x07})};
    if (expect_test_mtc_is_used()) {
      // Once a Chrome Root Store update containing the matching MtcAnchor is
      // loaded, the landmark relative MTC trust anchor IDs should be usable
      // immediately. The MTC CA that did not have landmark data should also be
      // advertised using the CA id.
      base::Extend(expected_trust_anchor_ids,
                   {base::ToVector(kMtcCaStandaloneId),
                    base::ToVector(kMtcCaInMetadataWithNoLandmarksId),
                    net::x509_util::CreateMtcLandmarkGroupTrustAnchorID(
                        kMtcCaWithLandmarksId, 2, 5)});
    }
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAreArray(expected_trust_anchor_ids));
  }
}

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentChromeRootStoreMtcMetadataTest,
                       TrustAnchorIDsWhenUpdateCRSBeforeMtcMetadata) {
  content::StoragePartition* partition =
      chrome_test_utils::GetActiveWebContents(this)
          ->GetBrowserContext()
          ->GetDefaultStoragePartition();
  int64_t crs_version = net::CompiledChromeRootStoreVersion();
  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  scoped_refptr<net::X509Certificate> intermediate1 = net::ImportCertFromFile(
      net::GetTestCertsDirectory(), "intermediate_ca_cert.pem");
  ASSERT_TRUE(intermediate1);
  scoped_refptr<net::X509Certificate> intermediate2 = net::ImportCertFromFile(
      net::GetTestCertsDirectory(), "verisign_intermediate_ca_2016.pem");
  ASSERT_TRUE(intermediate2);

  // Test that the initial set of Trust Anchor IDs comes from the compiled-in
  // root store.
  {
    std::vector<std::vector<uint8_t>> expected_trust_anchor_ids =
        net::TrustStoreChrome::GetTrustAnchorIDsFromCompiledInRootStore();
    if (mtcs_enabled()) {
      base::Extend(
          expected_trust_anchor_ids,
          net::TrustStoreChrome::GetTrustedMtcCaIDsFromCompiledInRootStore());
    }
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAreArray(expected_trust_anchor_ids));
  }

  static constexpr uint8_t kMtcCaWithLandmarksId[] = {0x01, 0x02, 0x03};
  static constexpr uint8_t kMtcCaInMetadataWithNoLandmarksId[] = {0x01, 0x02,
                                                                  0x04};
  static constexpr uint8_t kMtcCaStandaloneId[] = {0x01, 0x02, 0x05};
  // Install CRS update that contains a trusted MtcAnchor matching the
  // MtcMetadata which will be added later, as well as a traditional anchor
  // with a TAI.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);

    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->set_trust_anchor_id({0x05, 0x06, 0x07});

    chrome_root_store::MtcConfig mtc_config;
    mtc_config.mutable_signer_set()->mutable_timestamp()->set_seconds(
        base::Time::Now().InSecondsFSinceUnixEpoch());
    net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                            kMtcCaStandaloneId, "op1", std::nullopt)
        ->set_realm(realm());
    net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                            kMtcCaWithLandmarksId, "op2", std::nullopt)
        ->set_realm(realm());
    net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                            kMtcCaInMetadataWithNoLandmarksId, "op3",
                            std::nullopt)
        ->set_realm(realm());

    InstallCRSUpdate(root_store_proto, mtc_config);

    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());

    std::vector<std::vector<uint8_t>> expected_trust_anchor_ids = {
        std::vector<uint8_t>({0x05, 0x06, 0x07})};
    if (expect_test_mtc_is_used()) {
      // The MTC metadata hasn't been loaded yet, so the MTC CAs should be
      // advertised by their CA ID.
      base::Extend(expected_trust_anchor_ids,
                   {base::ToVector(kMtcCaStandaloneId),
                    base::ToVector(kMtcCaInMetadataWithNoLandmarksId),
                    base::ToVector(kMtcCaWithLandmarksId)});
    }
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAreArray(expected_trust_anchor_ids));
  }

  // Install MTC metadata update that contains Trust AnchorIDs for
  // landmark relative MTCs. Since the SignerSet was already loaded, the
  // landmark relative TAIs should be used immediately.
  {
    chrome_root_store::MtcMetadata mtc_metadata_proto;
    mtc_metadata_proto.set_update_time_seconds(
        SecondsSinceEpoch(base::Time::Now()));
    {
      chrome_root_store::MtcAnchorData* mtc_anchor_metadata =
          mtc_metadata_proto.add_mtc_anchor_data();
      mtc_anchor_metadata->set_ca_id(
          base::as_string_view(kMtcCaWithLandmarksId));
      chrome_root_store::MtcLogData* log_data =
          mtc_anchor_metadata->add_mtc_log_data();
      log_data->set_log_number(2);
      log_data->mutable_trusted_landmark_ids_range()
          ->set_min_active_landmark_inclusive(3);
      log_data->mutable_trusted_landmark_ids_range()
          ->set_last_landmark_inclusive(5);
      auto* subtree = log_data->add_trusted_subtrees();
      subtree->set_start_inclusive(0);
      subtree->set_end_exclusive(1);
      subtree->set_hash(std::string(32, 'a'));
    }
    {
      chrome_root_store::MtcAnchorData* mtc_anchor_metadata =
          mtc_metadata_proto.add_mtc_anchor_data();
      mtc_anchor_metadata->set_ca_id(
          base::as_string_view(kMtcCaInMetadataWithNoLandmarksId));
      chrome_root_store::MtcIndexRange* revoked_range =
          mtc_anchor_metadata->add_revoked_indices();
      revoked_range->set_start_inclusive(5);
      revoked_range->set_end_exclusive(10);
    }

    InstallMtcMetadataUpdate(mtc_metadata_proto);
    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    std::vector<std::vector<uint8_t>> expected_trust_anchor_ids = {
        std::vector<uint8_t>({0x05, 0x06, 0x07})};
    if (expect_test_mtc_is_used()) {
      // Once a Chrome Root Store update containing the matching MtcAnchor is
      // loaded, the landmark relative MTC trust anchor IDs should be usable
      // immediately. The MTC CA that did not have landmark data should also be
      // advertised using the CA id.
      base::Extend(expected_trust_anchor_ids,
                   {base::ToVector(kMtcCaStandaloneId),
                    base::ToVector(kMtcCaInMetadataWithNoLandmarksId),
                    net::x509_util::CreateMtcLandmarkGroupTrustAnchorID(
                        kMtcCaWithLandmarksId, 2, 5)});
    }
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAreArray(expected_trust_anchor_ids));
  }
}

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentChromeRootStoreMtcMetadataTest,
                       StaleMtcMetadata) {
  content::StoragePartition* partition =
      chrome_test_utils::GetActiveWebContents(this)
          ->GetBrowserContext()
          ->GetDefaultStoragePartition();
  int64_t crs_version = net::CompiledChromeRootStoreVersion();
  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  scoped_refptr<net::X509Certificate> intermediate1 = net::ImportCertFromFile(
      net::GetTestCertsDirectory(), "intermediate_ca_cert.pem");
  ASSERT_TRUE(intermediate1);
  scoped_refptr<net::X509Certificate> intermediate2 = net::ImportCertFromFile(
      net::GetTestCertsDirectory(), "verisign_intermediate_ca_2016.pem");
  ASSERT_TRUE(intermediate2);

  // Test that the initial set of Trust Anchor IDs comes from the compiled-in
  // root store.
  {
    std::vector<std::vector<uint8_t>> expected_trust_anchor_ids =
        net::TrustStoreChrome::GetTrustAnchorIDsFromCompiledInRootStore();
    if (mtcs_enabled()) {
      base::Extend(
          expected_trust_anchor_ids,
          net::TrustStoreChrome::GetTrustedMtcCaIDsFromCompiledInRootStore());
    }
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    EXPECT_THAT(future.Get(),
                testing::UnorderedElementsAreArray(expected_trust_anchor_ids));
  }

  static constexpr uint8_t kMtcCaWithLandmarksId[] = {0x01, 0x02, 0x03};
  // Install CRS update that contains a trusted MtcAnchor matching the
  // MtcMetadata which will be added later, as well as a traditional anchor
  // with a TAI.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);

    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->set_trust_anchor_id({0x05, 0x06, 0x07});

    chrome_root_store::MtcConfig mtc_config;
    mtc_config.mutable_signer_set()->mutable_timestamp()->set_seconds(
        base::Time::Now().InSecondsFSinceUnixEpoch());
    net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                            kMtcCaWithLandmarksId, "op2", std::nullopt)
        ->set_realm(realm());

    InstallCRSUpdate(root_store_proto, mtc_config);

    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    if (expect_test_mtc_is_used()) {
      // MTCMetadata hasn't been loaded, so the TAI have the MTC CA ID instead
      // of the landmark group ID.
      EXPECT_THAT(future.Get(), testing::UnorderedElementsAre(
                                    std::vector<uint8_t>({0x05, 0x06, 0x07}),
                                    base::ToVector(kMtcCaWithLandmarksId)));
    } else {
      EXPECT_THAT(future.Get(), testing::UnorderedElementsAre(
                                    std::vector<uint8_t>({0x05, 0x06, 0x07})));
    }
  }

  // Populate a MtcMetadata proto that will be used as the base for each of the
  // following test cases. The test cases should make a copy and then modify
  // that copy, leaving the base unchanged for use by the next case.
  chrome_root_store::MtcMetadata base_mtc_metadata_proto;
  {
    chrome_root_store::MtcAnchorData* mtc_anchor_metadata =
        base_mtc_metadata_proto.add_mtc_anchor_data();
    mtc_anchor_metadata->set_ca_id(base::as_string_view(kMtcCaWithLandmarksId));
    chrome_root_store::MtcLogData* log_data =
        mtc_anchor_metadata->add_mtc_log_data();
    log_data->set_log_number(2);
    log_data->mutable_trusted_landmark_ids_range()
        ->set_min_active_landmark_inclusive(3);
    log_data->mutable_trusted_landmark_ids_range()->set_last_landmark_inclusive(
        5);
    auto* subtree = log_data->add_trusted_subtrees();
    subtree->set_start_inclusive(0);
    subtree->set_end_exclusive(1);
    subtree->set_hash(std::string(32, 'a'));
  }
  // Attempt to install MTC metadata update that contains Trust Anchor IDs for
  // landmark relative MTCs, but which has an out-of-date update time. It
  // should be ignored.
  {
    chrome_root_store::MtcMetadata old_mtc_metadata_proto =
        base_mtc_metadata_proto;
    old_mtc_metadata_proto.set_update_time_seconds(
        SecondsSinceEpoch(base::Time::Now() - base::Days(49)));
    InstallMtcMetadataUpdate(old_mtc_metadata_proto);
    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    if (expect_test_mtc_is_used()) {
      // MTCMetadata update should have been ignored, so the TAI have the MTC CA
      // ID instead of the landmark group ID.
      EXPECT_THAT(future.Get(), testing::UnorderedElementsAre(
                                    std::vector<uint8_t>({0x05, 0x06, 0x07}),
                                    base::ToVector(kMtcCaWithLandmarksId)));
    } else {
      EXPECT_THAT(future.Get(), testing::UnorderedElementsAre(
                                    std::vector<uint8_t>({0x05, 0x06, 0x07})));
    }
  }

  // Install a new MTC metadata update that contains Trust Anchor IDs for
  // landmark relative MTCs and which is up to date.
  {
    chrome_root_store::MtcMetadata new_mtc_metadata_proto =
        base_mtc_metadata_proto;
    new_mtc_metadata_proto.set_update_time_seconds(
        SecondsSinceEpoch(base::Time::Now()));
    InstallMtcMetadataUpdate(new_mtc_metadata_proto);
    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    if (expect_test_mtc_is_used()) {
      EXPECT_THAT(future.Get(),
                  testing::UnorderedElementsAre(
                      std::vector<uint8_t>({0x05, 0x06, 0x07}),
                      net::x509_util::CreateMtcLandmarkGroupTrustAnchorID(
                          kMtcCaWithLandmarksId, 2, 5)));
    } else {
      EXPECT_THAT(future.Get(), testing::UnorderedElementsAre(
                                    std::vector<uint8_t>({0x05, 0x06, 0x07})));
    }
  }

  // Attempt to install another stale MTC metadata update that contains Trust
  // Anchor IDs for landmark relative MTCs. It should be ignored.
  {
    chrome_root_store::MtcMetadata old_mtc_metadata_proto =
        base_mtc_metadata_proto;
    old_mtc_metadata_proto.set_update_time_seconds(
        SecondsSinceEpoch(base::Time::Now() - base::Days(48)));
    // The base_mtc_metadata_proto has log_number 2. Set it to 1 in this update
    // so the test can distinguish whether this proto was used or the previous
    // update is still being used.
    old_mtc_metadata_proto.mutable_mtc_anchor_data(0)
        ->mutable_mtc_log_data(0)
        ->set_log_number(1);
    InstallMtcMetadataUpdate(old_mtc_metadata_proto);
    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
    base::test::TestFuture<const std::vector<std::vector<uint8_t>>&> future;
    partition->GetNetworkContext()->GetTrustAnchorIDsForTesting(
        future.GetCallback());
    // This MTCMetadata update should have been ignored, so the TAI will still
    // be from the previous successful update. (This is slightly weird test
    // scenario since you wouldn't normally expect to have a still-valid
    // component and then be served an older, out-of-date one.)
    if (expect_test_mtc_is_used()) {
      EXPECT_THAT(future.Get(),
                  testing::UnorderedElementsAre(
                      std::vector<uint8_t>({0x05, 0x06, 0x07}),
                      net::x509_util::CreateMtcLandmarkGroupTrustAnchorID(
                          kMtcCaWithLandmarksId, 2, 5)));
    } else {
      EXPECT_THAT(future.Get(), testing::UnorderedElementsAre(
                                    std::vector<uint8_t>({0x05, 0x06, 0x07})));
    }
  }
}

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentChromeRootStoreMtcMetadataTest,
                       EndToEnd) {
  static constexpr char kHostname[] = "www.example.com";
  static constexpr uint8_t kMtcCaId[] = {0x09, 0x08, 0x07};
  static constexpr uint8_t kMirrorId[] = {0x01, 0x02, 0x03};

  int64_t crs_version = net::CompiledChromeRootStoreVersion();

  net::MtcLogBuilder::Cosigner ca_cosigner = {
      base::ToVector(kMtcCaId), crypto::keypair::PrivateKey::GenerateMldsa44(),
      bssl::SignatureAlgorithm::kMldsa44};
  net::MtcLogBuilder::Cosigner mirror_cosigner = {
      base::ToVector(kMirrorId), crypto::keypair::PrivateKey::GenerateMldsa44(),
      bssl::SignatureAlgorithm::kMldsa44};

  net::MtcLogBuilder mtc_log(kMtcCaId, /*log_number=*/1);
  // TODO(crbug.com/469624806): improve interface for creating MTC cert
  // builders.
  std::unique_ptr<net::CertBuilder> mtc_leaf =
      std::move(net::CertBuilder::CreateSimpleChain(1u)[0]);
  mtc_leaf->SetSubjectAltName(kHostname);

  mtc_log.AddUnusedEntries(21);
  uint64_t mtc_log_index = mtc_log.AddEntry(*mtc_leaf);
  mtc_log.AddUnusedEntries(7);
  mtc_log.AdvanceLandmark();

  // Second log builder, but with the same log id, will be used to generate a
  // MTC leaf cert with the same subject/index/issuer, but with a different
  // proof.
  net::MtcLogBuilder different_mtc_log(kMtcCaId, /*log_number=*/1);
  different_mtc_log.AddUnusedEntries(21, {0x02});
  uint64_t different_mtc_log_index = different_mtc_log.AddEntry(*mtc_leaf);
  different_mtc_log.AddUnusedEntries(7, {0x02});
  different_mtc_log.AdvanceLandmark();
  ASSERT_EQ(mtc_log_index, different_mtc_log_index);

  // Test part 1:
  // CRS only trusts legacy test root.
  // Server has both legacy cert and new cert.
  // Client should send no trust anchor id, server should send legacy cert.

  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig legacy_cert_config;
  legacy_cert_config.dns_names = {kHostname};
  legacy_cert_config.root = net::EmbeddedTestServer::RootType::kUniqueRoot;

  net::EmbeddedTestServer::ServerCertificateConfig mtc_landmark_cert_config;
  mtc_landmark_cert_config.trust_anchor_id =
      mtc_log.GetLandmarkTrustAnchorGroup();
  auto mtc_landmark_cert =
      mtc_log.CreateSignaturelessCertificateBuffer(mtc_log_index);
  ASSERT_TRUE(mtc_landmark_cert);
  mtc_landmark_cert_config.cert_and_key = net::EmbeddedTestServer::CertAndKey(
      bssl::UpRef(mtc_landmark_cert), bssl::UpRef(mtc_leaf->GetKey()));

  net::EmbeddedTestServer::ServerCertificateConfig mtc_standalone_cert_config;
  mtc_standalone_cert_config.trust_anchor_id = base::ToVector(mtc_log.ca_id());
  auto mtc_standalone_cert = mtc_log.CreateStandaloneCertificateBuffer(
      mtc_log_index, {&ca_cosigner, &mirror_cosigner});
  ASSERT_TRUE(mtc_standalone_cert);
  mtc_standalone_cert_config.cert_and_key = net::EmbeddedTestServer::CertAndKey(
      bssl::UpRef(mtc_standalone_cert), bssl::UpRef(mtc_leaf->GetKey()));

  net::SSLServerConfig server_config;
  server_config.client_hello_callback_for_testing =
      base::BindRepeating(&LogClientHelloTrustAnchorIDs);

  https_server_ok.SetSSLConfig({mtc_landmark_cert_config,
                                mtc_standalone_cert_config, legacy_cert_config},
                               server_config);
  constexpr size_t kMtcLandmarkCertConfigNumber = 0;
  constexpr size_t kMtcStandaloneCertConfigNumber = 1;
  constexpr size_t kLegacyCertConfigNumber = 2;
  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");

  ASSERT_TRUE(https_server_ok.Start());

  // A test server that is only configured with the MTC cert.
  net::EmbeddedTestServer mtc_only_server(net::EmbeddedTestServer::TYPE_HTTPS);
  // Same as mtc_cert_config, but doesn't specify trust_anchor_id since this in
  // the only config this server is configured with.
  net::EmbeddedTestServer::ServerCertificateConfig mtc_only_cert_config;
  mtc_only_cert_config.cert_and_key = net::EmbeddedTestServer::CertAndKey(
      bssl::UpRef(mtc_landmark_cert), bssl::UpRef(mtc_leaf->GetKey()));
  mtc_only_server.SetSSLConfig({mtc_only_cert_config}, server_config);
  mtc_only_server.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(mtc_only_server.Start());

  // A test server that is configured with an MTC cert with the same
  // subject/issuer/index but with a different proof.
  net::EmbeddedTestServer different_mtc_only_server(
      net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig different_mtc_cert_config;
  auto different_mtc_cert =
      different_mtc_log.CreateSignaturelessCertificateBuffer(
          different_mtc_log_index);
  ASSERT_TRUE(different_mtc_cert);
  different_mtc_cert_config.cert_and_key = net::EmbeddedTestServer::CertAndKey(
      bssl::UpRef(different_mtc_cert), bssl::UpRef(mtc_leaf->GetKey()));
  different_mtc_only_server.SetSSLConfig({different_mtc_cert_config},
                                         server_config);
  different_mtc_only_server.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(different_mtc_only_server.Start());

  scoped_refptr<net::X509Certificate> legacy_root_cert =
      https_server_ok.GetRoot(kLegacyCertConfigNumber);
  ASSERT_TRUE(legacy_root_cert);

  chrome_root_store::RootStore root_store_proto;
  root_store_proto.set_version_major(++crs_version);
  root_store_proto.add_trust_anchors()->set_der(
      std::string(net::x509_util::CryptoBufferAsStringPiece(
          legacy_root_cert->cert_buffer())));

  // Install CRS proto with only the legacy anchor.
  {
    InstallCRSUpdate(root_store_proto);

    // Ensure that SSLConfigClients have been notified of the any trust anchor
    // IDs (although there shouldn't be any configured yet.)
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
  }

  {
    // Loading the test server should succeed using the legacy cert & anchor.
    CertificateCheckingThrottleController certificate_observer;
    certificate_observer.InsertThrottleExpectingCertificate(
        chrome_test_utils::GetActiveWebContents(this),
        https_server_ok.GetCertificate(kLegacyCertConfigNumber));
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), https_server_ok.GetURL(kHostname, "/simple.html")));
    EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticatedState(
        chrome_test_utils::GetActiveWebContents(this),
        ssl_test_util::AuthState::NONE);
    ASSERT_GT(certificate_observer.num_observed_responses(), 0u);
  }

  {
    // Attempt to load from the server which only has the MTC cert. This should
    // fail since the MTC anchor is not trusted yet.
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), mtc_only_server.GetURL(kHostname, "/simple.html")));
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  }

  // Test part 2:
  // Configure CRS update that has the MTC anchor and mirror.
  // Client should advertise the standalone MTC CA TAI.
  constexpr int32_t kFakeCrsRootId = 98700;

  {
    chrome_root_store::MtcConfig mtc_config;
    mtc_config.mutable_signer_set()->mutable_timestamp()->set_seconds(
        base::Time::Now().InSecondsFSinceUnixEpoch());
    auto* issuer = net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                                           kMtcCaId, "op1", kFakeCrsRootId);
    issuer->set_realm(realm());
    issuer->set_signature_algorithm(
        chrome_root_store::SIGNATURE_ALGORITHM_ML_DSA44);
    issuer->set_key(
        base::as_string_view(ca_cosigner.key.ToSubjectPublicKeyInfo()));

    auto* mirror = net::AddSignerSetMirror(*mtc_config.mutable_signer_set(),
                                           kMirrorId, "op2");
    mirror->set_realm(realm());
    mirror->set_signature_algorithm(
        chrome_root_store::SIGNATURE_ALGORITHM_ML_DSA44);
    mirror->set_key(
        base::as_string_view(mirror_cosigner.key.ToSubjectPublicKeyInfo()));

    InstallCRSUpdate(root_store_proto, mtc_config);
  }

  {
    content::WebContents* web_contents =
        chrome_test_utils::GetActiveWebContents(this);
    CertificateCheckingThrottleController certificate_observer;
    if (expect_test_mtc_is_used()) {
      // If MTC feature is enabled, the client should have advertised the
      // standalone MTC TAI and the server should send the standalone MTC cert.
      certificate_observer.InsertThrottleExpectingCertificate(
          web_contents,
          https_server_ok.GetCertificate(kMtcStandaloneCertConfigNumber));
    } else {
      // If the client didn't advertise the MTC TAI, the server should send the
      // legacy cert.
      certificate_observer.InsertThrottleExpectingCertificate(
          web_contents,
          https_server_ok.GetCertificate(kLegacyCertConfigNumber));
    }
    base::HistogramTester histograms;
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), https_server_ok.GetURL(kHostname, "/title2.html")));
    EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
              u"Title Of Awesomeness");
    ASSERT_GT(certificate_observer.num_observed_responses(), 0u);
    if (expect_test_mtc_is_used()) {
      // If the MTC was used, the histograms for the MTC anchor CRS ID should
      // have been recorded.
      metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
      EXPECT_GE(histograms.GetBucketCount(
                    "Net.Certificate.TrustAnchor2.Request", kFakeCrsRootId),
                1u);
      histograms.ExpectUniqueSample("Net.Certificate.TrustAnchor2.Verify",
                                    kFakeCrsRootId, 1u);
    }
  }
  {
    // Attempt to load from the server which only has the landmark relative MTC
    // cert and doesn't use trust anchor IDs. This should fail since the MTC
    // Metadata isn't loaded yet.
    CertificateCheckingThrottleController certificate_observer;
    certificate_observer.InsertThrottleExpectingCertificate(
        chrome_test_utils::GetActiveWebContents(this),
        mtc_only_server.GetCertificate());
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), mtc_only_server.GetURL(kHostname, "/simple.html")));
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  }

  // Test part 3:
  // Configure MTC metadata.
  // Client should advertise the MTC landmark group Trust Anchor ID, server
  // should send matching MTC cert.

  // Install fastpush proto with the MTC anchor metadata.
  {
    chrome_root_store::MtcMetadata mtc_metadata_proto;
    mtc_metadata_proto.set_update_time_seconds(
        SecondsSinceEpoch(base::Time::Now()));
    mtc_log.FillMtcMetadataAnchorProto(
        mtc_metadata_proto.add_mtc_anchor_data());

    InstallMtcMetadataUpdate(mtc_metadata_proto);

    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
  }

  {
    content::WebContents* web_contents =
        chrome_test_utils::GetActiveWebContents(this);
    CertificateCheckingThrottleController certificate_observer;
    if (expect_test_mtc_is_used()) {
      // If MTC feature is enabled, the client should have advertised the
      // landmark group MTC TAI and the server should send the landmark
      // relative MTC cert.
      certificate_observer.InsertThrottleExpectingCertificate(
          web_contents,
          https_server_ok.GetCertificate(kMtcLandmarkCertConfigNumber));
    } else {
      // If the client didn't advertise the MTC TAI, the server should send the
      // legacy cert.
      certificate_observer.InsertThrottleExpectingCertificate(
          web_contents,
          https_server_ok.GetCertificate(kLegacyCertConfigNumber));
    }
    base::HistogramTester histograms;
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), https_server_ok.GetURL(kHostname, "/title2.html")));
    EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
              u"Title Of Awesomeness");
    ASSERT_GT(certificate_observer.num_observed_responses(), 0u);
    if (expect_test_mtc_is_used()) {
      // If the MTC was used, the histograms for the MTC anchor CRS ID should
      // have been recorded.
      metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
      EXPECT_GE(histograms.GetBucketCount(
                    "Net.Certificate.TrustAnchor2.Request", kFakeCrsRootId),
                1u);
      histograms.ExpectUniqueSample("Net.Certificate.TrustAnchor2.Verify",
                                    kFakeCrsRootId, 1u);
    }
  }

  {
    // Attempt to load from the server which only has the landmark relative MTC
    // cert and doesn't use trust anchor IDs. This should succeed if MTCs are
    // enabled, otherwise it should fail.
    CertificateCheckingThrottleController certificate_observer;
    certificate_observer.InsertThrottleExpectingCertificate(
        chrome_test_utils::GetActiveWebContents(this),
        mtc_only_server.GetCertificate());
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), mtc_only_server.GetURL(kHostname, "/simple.html")));
    if (expect_test_mtc_is_used()) {
      EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                u"OK");
      ssl_test_util::CheckAuthenticatedState(
          chrome_test_utils::GetActiveWebContents(this),
          ssl_test_util::AuthState::NONE);
      ASSERT_GT(certificate_observer.num_observed_responses(), 0u);
    } else {
      EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                u"OK");
      ssl_test_util::CheckAuthenticationBrokenState(
          chrome_test_utils::GetActiveWebContents(this),
          net::CERT_STATUS_AUTHORITY_INVALID,
          ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
    }
  }

  {
    // Attempt to load from the server which only has the landmark relative MTC
    // cert with an incorrect proof. This should fail.
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(),
        different_mtc_only_server.GetURL(kHostname, "/simple.html")));
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  }
}

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentChromeRootStoreMtcMetadataTest,
                       Revocation) {
  static constexpr uint8_t kMtcCaId[] = {0x09, 0x08, 0x07};
  static constexpr uint8_t kMirrorId[] = {0x01, 0x02, 0x03};
  static constexpr uint64_t kLogNumber = 1;

  int64_t crs_version = net::CompiledChromeRootStoreVersion();

  struct TestCertData {
    std::string hostname;
    std::unique_ptr<net::CertBuilder> builder;
    uint64_t mtc_log_index = 0;
    uint64_t mtc_serial = 0;
    bssl::UniquePtr<CRYPTO_BUFFER> landmark_cert_buffer;
    net::EmbeddedTestServer landmark_and_legacy_server{
        net::EmbeddedTestServer::TYPE_HTTPS};
    net::EmbeddedTestServer landmark_only_server{
        net::EmbeddedTestServer::TYPE_HTTPS};
    bssl::UniquePtr<CRYPTO_BUFFER> standalone_cert_buffer;
    net::EmbeddedTestServer standalone_only_server{
        net::EmbeddedTestServer::TYPE_HTTPS};
    bool expect_is_revoked = false;
  };
  std::array<TestCertData, 6> test_cert_data;

  net::MtcLogBuilder::Cosigner ca_cosigner = {
      base::ToVector(kMtcCaId), crypto::keypair::PrivateKey::GenerateMldsa44(),
      bssl::SignatureAlgorithm::kMldsa44};
  net::MtcLogBuilder::Cosigner mirror_cosigner = {
      base::ToVector(kMirrorId), crypto::keypair::PrivateKey::GenerateMldsa44(),
      bssl::SignatureAlgorithm::kMldsa44};

  net::MtcLogBuilder mtc_log(kMtcCaId, kLogNumber);

  for (int i = 0; i < test_cert_data.size(); ++i) {
    TestCertData& data = test_cert_data[i];
    // TODO(crbug.com/469624806): improve interface for creating MTC cert
    // builders.
    data.hostname = absl::StrFormat("www%d.example.com", i);
    data.builder = std::move(net::CertBuilder::CreateSimpleChain(1u)[0]);
    data.builder->SetSubjectAltName(data.hostname);
    data.mtc_log_index = mtc_log.AddEntry(*data.builder);
    data.mtc_serial = (kLogNumber << 48) + data.mtc_log_index;
  }

  mtc_log.AdvanceLandmark();

  for (TestCertData& data : test_cert_data) {
    net::EmbeddedTestServer::ServerCertificateConfig legacy_cert_config;
    legacy_cert_config.dns_names = {data.hostname};
    legacy_cert_config.root = net::EmbeddedTestServer::RootType::kUniqueRoot;

    net::EmbeddedTestServer::ServerCertificateConfig landmark_cert_config;
    landmark_cert_config.trust_anchor_id =
        mtc_log.GetLandmarkTrustAnchorGroup();
    data.landmark_cert_buffer =
        mtc_log.CreateSignaturelessCertificateBuffer(data.mtc_log_index);
    ASSERT_TRUE(data.landmark_cert_buffer);
    landmark_cert_config.cert_and_key = net::EmbeddedTestServer::CertAndKey(
        bssl::UpRef(data.landmark_cert_buffer),
        bssl::UpRef(data.builder->GetKey()));

    net::SSLServerConfig server_config;
    server_config.client_hello_callback_for_testing =
        base::BindRepeating(&LogClientHelloTrustAnchorIDs);

    data.landmark_and_legacy_server.SetSSLConfig(
        {landmark_cert_config, legacy_cert_config}, server_config);
    data.landmark_and_legacy_server.ServeFilesFromSourceDirectory(
        "chrome/test/data");
    ASSERT_TRUE(data.landmark_and_legacy_server.Start());

    // Same as landmark_cert_config, but doesn't specify trust_anchor_id since
    // this is the only config this server is configured with.
    net::EmbeddedTestServer::ServerCertificateConfig mtc_only_cert_config;
    mtc_only_cert_config.cert_and_key = net::EmbeddedTestServer::CertAndKey(
        bssl::UpRef(data.landmark_cert_buffer),
        bssl::UpRef(data.builder->GetKey()));
    data.landmark_only_server.SetSSLConfig({mtc_only_cert_config},
                                           server_config);
    data.landmark_only_server.ServeFilesFromSourceDirectory("chrome/test/data");
    ASSERT_TRUE(data.landmark_only_server.Start());

    // Same as landmark_only_server, but with the standalone MTC.
    net::EmbeddedTestServer::ServerCertificateConfig
        standalone_only_cert_config;
    data.standalone_cert_buffer = mtc_log.CreateStandaloneCertificateBuffer(
        data.mtc_log_index, {&ca_cosigner, &mirror_cosigner});
    ASSERT_TRUE(data.standalone_cert_buffer);
    standalone_only_cert_config.cert_and_key =
        net::EmbeddedTestServer::CertAndKey(
            bssl::UpRef(data.standalone_cert_buffer),
            bssl::UpRef(data.builder->GetKey()));

    data.standalone_only_server.SetSSLConfig({standalone_only_cert_config},
                                             server_config);
    data.standalone_only_server.ServeFilesFromSourceDirectory(
        "chrome/test/data");
    ASSERT_TRUE(data.standalone_only_server.Start());
  }

  constexpr size_t kMtcCertConfigNumber = 0;
  constexpr size_t kLegacyCertConfigNumber = 1;

  // Install CRS proto with the MTC anchor and the legacy anchors.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);

    for (TestCertData& data : test_cert_data) {
      scoped_refptr<net::X509Certificate> legacy_root_cert =
          data.landmark_and_legacy_server.GetRoot(kLegacyCertConfigNumber);
      ASSERT_TRUE(legacy_root_cert);
      root_store_proto.add_trust_anchors()->set_der(
          std::string(net::x509_util::CryptoBufferAsStringPiece(
              legacy_root_cert->cert_buffer())));
    }

    chrome_root_store::MtcConfig mtc_config;
    mtc_config.mutable_signer_set()->mutable_timestamp()->set_seconds(
        base::Time::Now().InSecondsFSinceUnixEpoch());

    auto* issuer = net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                                           kMtcCaId, "op1", std::nullopt);
    issuer->set_realm(realm());
    issuer->set_signature_algorithm(
        chrome_root_store::SIGNATURE_ALGORITHM_ML_DSA44);
    issuer->set_key(
        base::as_string_view(ca_cosigner.key.ToSubjectPublicKeyInfo()));

    auto* mirror = net::AddSignerSetMirror(*mtc_config.mutable_signer_set(),
                                           kMirrorId, "op2");
    mirror->set_realm(realm());
    mirror->set_signature_algorithm(
        chrome_root_store::SIGNATURE_ALGORITHM_ML_DSA44);
    mirror->set_key(
        base::as_string_view(mirror_cosigner.key.ToSubjectPublicKeyInfo()));

    InstallCRSUpdate(root_store_proto, mtc_config);
  }

  // Install fastpush proto with the MTC anchor metadata.
  {
    chrome_root_store::MtcMetadata mtc_metadata_proto;
    mtc_metadata_proto.set_update_time_seconds(
        SecondsSinceEpoch(base::Time::Now()));
    auto* mtc_anchor_data = mtc_metadata_proto.add_mtc_anchor_data();
    mtc_log.FillMtcMetadataAnchorProto(mtc_anchor_data);

    // Add revoked range that contains 1 certificate.
    {
      auto* revoked_range = mtc_anchor_data->add_revoked_indices();
      revoked_range->set_start_inclusive(test_cert_data[1].mtc_serial);
      revoked_range->set_end_exclusive(test_cert_data[2].mtc_serial);
      test_cert_data[1].expect_is_revoked = true;
    }

    // Add revoked range that contains multiple certificates.
    {
      auto* revoked_range = mtc_anchor_data->add_revoked_indices();
      revoked_range->set_start_inclusive(test_cert_data[3].mtc_serial);
      revoked_range->set_end_exclusive(test_cert_data[5].mtc_serial);
      test_cert_data[3].expect_is_revoked = true;
      test_cert_data[4].expect_is_revoked = true;
    }

    InstallMtcMetadataUpdate(mtc_metadata_proto);

    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
  }

  for (TestCertData& data : test_cert_data) {
    SCOPED_TRACE(data.hostname);

    {
      // Attempt to load from the server which is configured with both the
      // landmark relative MTC and a legacy certificate.
      net::RecordingNetLogObserver net_log_observer;
      ASSERT_TRUE(ui_test_utils::NavigateToURL(
          browser(), data.landmark_and_legacy_server.GetURL(data.hostname,
                                                            "/title2.html")));
      std::vector<std::string> observed_cert_pems =
          GetNetLogCertPemChainsForHost(net_log_observer, data.hostname);
      if (!expect_test_mtc_is_used()) {
        // If the client didn't advertise the MTC TAI, the server should send
        // the legacy cert, which should succeed.
        EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                  u"Title Of Awesomeness");
        EXPECT_THAT(observed_cert_pems,
                    testing::ElementsAre(X509CertificateToString(
                        data.landmark_and_legacy_server.GetCertificate(
                            kLegacyCertConfigNumber))));
      } else if (data.expect_is_revoked) {
        // If MTC feature is enabled and the MTC is revoked, the client
        // should have advertised the MTC TAI and the server should send the
        // MTC cert which should fail to verify. Since we no longer retry
        // without the MTC TAI, the connection should simply fail.
        EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                  u"Title Of Awesomeness");
        ssl_test_util::CheckAuthenticationBrokenState(
            chrome_test_utils::GetActiveWebContents(this),
            net::CERT_STATUS_REVOKED,
            ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
        EXPECT_THAT(observed_cert_pems,
                    testing::ElementsAre(X509CertificateToString(
                        data.landmark_and_legacy_server.GetCertificate(
                            kMtcCertConfigNumber))));
      } else {
        // If MTC feature is enabled and the MTC is not revoked, the client
        // should have advertised the MTC TAI and the server should send the MTC
        // cert which should verify successufully.
        EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                  u"Title Of Awesomeness");
        EXPECT_THAT(observed_cert_pems,
                    testing::ElementsAre(X509CertificateToString(
                        data.landmark_and_legacy_server.GetCertificate(
                            kMtcCertConfigNumber))));
      }
    }

    {
      // Attempt to load from the server which only has the landmark relative
      // MTC cert and doesn't use trust anchor IDs. This should succeed if MTCs
      // are enabled and the cert is not revoked, otherwise it should fail.
      ASSERT_TRUE(ui_test_utils::NavigateToURL(
          browser(),
          data.landmark_only_server.GetURL(data.hostname, "/simple.html")));
      if (!expect_test_mtc_is_used()) {
        EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                  u"OK");
        ssl_test_util::CheckAuthenticationBrokenState(
            chrome_test_utils::GetActiveWebContents(this),
            net::CERT_STATUS_AUTHORITY_INVALID,
            ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
      } else if (data.expect_is_revoked) {
        EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                  u"OK");
        ssl_test_util::CheckAuthenticationBrokenState(
            chrome_test_utils::GetActiveWebContents(this),
            net::CERT_STATUS_REVOKED,
            ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
      } else {
        EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                  u"OK");
        ssl_test_util::CheckAuthenticatedState(
            chrome_test_utils::GetActiveWebContents(this),
            ssl_test_util::AuthState::NONE);
      }
    }

    {
      // Attempt to load from the server which only has the standalone MTC cert
      // and doesn't use trust anchor IDs. This should succeed if MTCs are
      // enabled and the cert is not revoked, otherwise it should fail.
      ASSERT_TRUE(ui_test_utils::NavigateToURL(
          browser(),
          data.standalone_only_server.GetURL(data.hostname, "/simple.html")));
      if (!expect_test_mtc_is_used()) {
        EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                  u"OK");
        ssl_test_util::CheckAuthenticationBrokenState(
            chrome_test_utils::GetActiveWebContents(this),
            net::CERT_STATUS_AUTHORITY_INVALID,
            ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
      } else if (data.expect_is_revoked) {
        EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                  u"OK");
        ssl_test_util::CheckAuthenticationBrokenState(
            chrome_test_utils::GetActiveWebContents(this),
            net::CERT_STATUS_REVOKED,
            ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
      } else {
        EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                  u"OK");
        ssl_test_util::CheckAuthenticatedState(
            chrome_test_utils::GetActiveWebContents(this),
            ssl_test_util::AuthState::NONE);
      }
    }
  }
}

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentChromeRootStoreMtcMetadataTest,
                       CosignerPolicy) {
  static constexpr char kHostname[] = "www.example.com";
  static constexpr uint8_t kMtcCaId[] = {0x09, 0x08, 0x07};
  static constexpr uint8_t kMirrorId[] = {0x01, 0x02, 0x03};

  int64_t crs_version = net::CompiledChromeRootStoreVersion();

  net::MtcLogBuilder::Cosigner ca_cosigner = {
      base::ToVector(kMtcCaId), crypto::keypair::PrivateKey::GenerateMldsa44(),
      bssl::SignatureAlgorithm::kMldsa44};
  net::MtcLogBuilder::Cosigner mirror_cosigner = {
      base::ToVector(kMirrorId), crypto::keypair::PrivateKey::GenerateMldsa44(),
      bssl::SignatureAlgorithm::kMldsa44};

  net::MtcLogBuilder mtc_log(kMtcCaId, /*log_number=*/1);
  // TODO(crbug.com/469624806): improve interface for creating MTC cert
  // builders.
  std::unique_ptr<net::CertBuilder> mtc_leaf =
      std::move(net::CertBuilder::CreateSimpleChain(1u)[0]);
  mtc_leaf->SetSubjectAltName(kHostname);

  mtc_log.AddUnusedEntries(21);
  uint64_t mtc_log_index = mtc_log.AddEntry(*mtc_leaf);
  mtc_log.AddUnusedEntries(7);
  mtc_log.AdvanceLandmark();

  net::EmbeddedTestServer::ServerCertificateConfig cert_config;
  auto mtc_standalone_cert = mtc_log.CreateStandaloneCertificateBuffer(
      mtc_log_index, {&ca_cosigner, &mirror_cosigner});
  ASSERT_TRUE(mtc_standalone_cert);
  cert_config.cert_and_key = net::EmbeddedTestServer::CertAndKey(
      bssl::UpRef(mtc_standalone_cert), bssl::UpRef(mtc_leaf->GetKey()));

  net::EmbeddedTestServer mtc_only_server(net::EmbeddedTestServer::TYPE_HTTPS);
  mtc_only_server.SetSSLConfig(cert_config);
  mtc_only_server.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(mtc_only_server.Start());

  // We reject empty CRS proto updates, so create a new cert root that doesn't
  // match what the test server uses.
  auto [leaf, root] = net::CertBuilder::CreateSimpleChain2();
  chrome_root_store::RootStore root_store_proto;
  root_store_proto.set_version_major(++crs_version);
  root_store_proto.add_trust_anchors()->set_der(root->GetDER());

  // Install update with the MTC CA, but without the necessary mirror.
  chrome_root_store::MtcConfig mtc_config;
  mtc_config.mutable_signer_set()->mutable_timestamp()->set_seconds(
      base::Time::Now().InSecondsFSinceUnixEpoch());
  auto* issuer = net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                                         kMtcCaId, "op1", std::nullopt);
  issuer->set_realm(realm());
  issuer->set_signature_algorithm(
      chrome_root_store::SIGNATURE_ALGORITHM_ML_DSA44);
  issuer->set_key(
      base::as_string_view(ca_cosigner.key.ToSubjectPublicKeyInfo()));

  InstallCRSUpdate(root_store_proto, mtc_config);

  {
    // Attempt to load should always fail: either MTCs are disabled, or if they
    // are enabled, the necessary mirror to satisfy cosigner policy isn't
    // available.
    CertificateCheckingThrottleController certificate_observer;
    certificate_observer.InsertThrottleExpectingCertificate(
        chrome_test_utils::GetActiveWebContents(this),
        mtc_only_server.GetCertificate());
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), mtc_only_server.GetURL(kHostname, "/simple.html")));
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  }

  // Install an update with the MtcConfig cosigner policy killswitch set.
  mtc_config.set_disable_mtc_mirroring_requirements(true);
  InstallCRSUpdate(root_store_proto, mtc_config);

  {
    // Attempt to load the page again now that mirroring isn't required due to
    // the killswitch.
    // This should succeed if MTCs are enabled, otherwise it should fail.
    CertificateCheckingThrottleController certificate_observer;
    certificate_observer.InsertThrottleExpectingCertificate(
        chrome_test_utils::GetActiveWebContents(this),
        mtc_only_server.GetCertificate());
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), mtc_only_server.GetURL(kHostname, "/simple.html")));
    if (expect_test_mtc_is_used()) {
      EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                u"OK");
      ssl_test_util::CheckAuthenticatedState(
          chrome_test_utils::GetActiveWebContents(this),
          ssl_test_util::AuthState::NONE);
      ASSERT_GT(certificate_observer.num_observed_responses(), 0u);
    } else {
      EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                u"OK");
      ssl_test_util::CheckAuthenticationBrokenState(
          chrome_test_utils::GetActiveWebContents(this),
          net::CERT_STATUS_AUTHORITY_INVALID,
          ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
    }
  }

  // Install another update, turning the killswitch back off again.
  mtc_config.set_disable_mtc_mirroring_requirements(false);
  InstallCRSUpdate(root_store_proto, mtc_config);

  {
    // Attempt to load should always fail: either MTCs are disabled, or if they
    // are enabled, the necessary mirror to satisfy cosigner policy isn't
    // available.
    CertificateCheckingThrottleController certificate_observer;
    certificate_observer.InsertThrottleExpectingCertificate(
        chrome_test_utils::GetActiveWebContents(this),
        mtc_only_server.GetCertificate());
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), mtc_only_server.GetURL(kHostname, "/simple.html")));
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  }

  // Finally, install an update with the necessary mirror.
  mtc_config.mutable_signer_set()->mutable_timestamp()->set_seconds(
      base::Time::Now().InSecondsFSinceUnixEpoch());
  auto* mirror = net::AddSignerSetMirror(*mtc_config.mutable_signer_set(),
                                         kMirrorId, "op2");
  mirror->set_realm(realm());
  mirror->set_signature_algorithm(
      chrome_root_store::SIGNATURE_ALGORITHM_ML_DSA44);
  mirror->set_key(
      base::as_string_view(mirror_cosigner.key.ToSubjectPublicKeyInfo()));
  InstallCRSUpdate(root_store_proto, mtc_config);

  {
    // Attempt to load the page again now that the mirror is provided.
    // This should succeed if MTCs are enabled, otherwise it should fail.
    CertificateCheckingThrottleController certificate_observer;
    certificate_observer.InsertThrottleExpectingCertificate(
        chrome_test_utils::GetActiveWebContents(this),
        mtc_only_server.GetCertificate());
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), mtc_only_server.GetURL(kHostname, "/simple.html")));
    if (expect_test_mtc_is_used()) {
      EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                u"OK");
      ssl_test_util::CheckAuthenticatedState(
          chrome_test_utils::GetActiveWebContents(this),
          ssl_test_util::AuthState::NONE);
      ASSERT_GT(certificate_observer.num_observed_responses(), 0u);
    } else {
      EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(),
                u"OK");
      ssl_test_util::CheckAuthenticationBrokenState(
          chrome_test_utils::GetActiveWebContents(this),
          net::CERT_STATUS_AUTHORITY_INVALID,
          ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
    }
  }
}

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentChromeRootStoreMtcMetadataTest,
                       MtcChromeRootStoreConstraints) {
  static constexpr char kHostname1[] = "www.example.com";
  static constexpr char kHostname2[] = "www.example.org";
  static constexpr char kHostname3[] = "other.example.org";
  static constexpr uint8_t kMtcCaId[] = {0x09, 0x08, 0x07};
  static constexpr uint64_t kLogNumber = 1;

  int64_t crs_version = net::CompiledChromeRootStoreVersion();

  net::MtcLogBuilder mtc_log(kMtcCaId, kLogNumber);

  // TODO(crbug.com/469624806): improve interface for creating MTC cert
  // builders.
  std::unique_ptr<net::CertBuilder> mtc_leaf1 =
      std::move(net::CertBuilder::CreateSimpleChain(1u)[0]);
  mtc_leaf1->SetSubjectAltName(kHostname1);
  uint64_t mtc_log_index1 = mtc_log.AddEntry(*mtc_leaf1);

  std::unique_ptr<net::CertBuilder> mtc_leaf2 =
      std::move(net::CertBuilder::CreateSimpleChain(1u)[0]);
  mtc_leaf2->SetSubjectAltName(kHostname2);
  uint64_t mtc_log_index2 = mtc_log.AddEntry(*mtc_leaf2);

  std::unique_ptr<net::CertBuilder> mtc_leaf3 =
      std::move(net::CertBuilder::CreateSimpleChain(1u)[0]);
  mtc_leaf3->SetSubjectAltName(kHostname3);
  uint64_t mtc_log_index3 = mtc_log.AddEntry(*mtc_leaf3);

  mtc_log.AdvanceLandmark();

  // Server using leaf 1.
  net::EmbeddedTestServer::ServerCertificateConfig mtc_cert_config1;
  auto mtc_cert1 = mtc_log.CreateSignaturelessCertificateBuffer(mtc_log_index1);
  ASSERT_TRUE(mtc_cert1);
  mtc_cert_config1.cert_and_key = net::EmbeddedTestServer::CertAndKey(
      bssl::UpRef(mtc_cert1), bssl::UpRef(mtc_leaf1->GetKey()));

  net::EmbeddedTestServer https_server_ok1(net::EmbeddedTestServer::TYPE_HTTPS);
  https_server_ok1.SetSSLConfig(mtc_cert_config1);
  https_server_ok1.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(https_server_ok1.Start());

  // Server using leaf 2.
  net::EmbeddedTestServer::ServerCertificateConfig mtc_cert_config2;
  auto mtc_cert2 = mtc_log.CreateSignaturelessCertificateBuffer(mtc_log_index2);
  ASSERT_TRUE(mtc_cert2);
  mtc_cert_config2.cert_and_key = net::EmbeddedTestServer::CertAndKey(
      bssl::UpRef(mtc_cert2), bssl::UpRef(mtc_leaf2->GetKey()));

  net::EmbeddedTestServer https_server_ok2(net::EmbeddedTestServer::TYPE_HTTPS);
  https_server_ok2.SetSSLConfig(mtc_cert_config2);
  https_server_ok2.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(https_server_ok2.Start());

  // Server using leaf 3.
  net::EmbeddedTestServer::ServerCertificateConfig mtc_cert_config3;
  auto mtc_cert3 = mtc_log.CreateSignaturelessCertificateBuffer(mtc_log_index3);
  ASSERT_TRUE(mtc_cert3);
  mtc_cert_config3.cert_and_key = net::EmbeddedTestServer::CertAndKey(
      bssl::UpRef(mtc_cert3), bssl::UpRef(mtc_leaf3->GetKey()));

  net::EmbeddedTestServer https_server_ok3(net::EmbeddedTestServer::TYPE_HTTPS);
  https_server_ok3.SetSSLConfig(mtc_cert_config3);
  https_server_ok3.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(https_server_ok3.Start());

  // Install CRS proto with the MTC anchor and an (unused) legacy anchor.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);

    // Need to add a classical anchor for the CRS proto to parse successfully,
    // it's not otherwise used by the test.
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    auto [unused_leaf, legacy_root] = net::CertBuilder::CreateSimpleChain2();
    anchor->set_der(legacy_root->GetDER());

    chrome_root_store::MtcConfig mtc_config;
    mtc_config.mutable_signer_set()->mutable_timestamp()->set_seconds(
        base::Time::Now().InSecondsFSinceUnixEpoch());
    auto* issuer = net::AddSignerSetIssuer(*mtc_config.mutable_signer_set(),
                                           kMtcCaId, "op1", std::nullopt);
    issuer->set_realm(realm());
    auto* constraint = issuer->add_constraints();
    constraint->add_permitted_dns_names("example.org");
    constraint->set_index_not_after((kLogNumber << 48) + mtc_log_index2);

    InstallCRSUpdate(root_store_proto, mtc_config);
  }

  // Install fastpush proto with the MTC anchor metadata.
  {
    chrome_root_store::MtcMetadata mtc_metadata_proto;
    mtc_metadata_proto.set_update_time_seconds(
        SecondsSinceEpoch(base::Time::Now()));
    chrome_root_store::MtcAnchorData* mtc_anchor_metadata =
        mtc_metadata_proto.add_mtc_anchor_data();
    mtc_log.FillMtcMetadataAnchorProto(mtc_anchor_metadata);

    InstallMtcMetadataUpdate(mtc_metadata_proto);

    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok1.GetURL(kHostname1, "/simple.html")));
  if (!expect_test_mtc_is_used()) {
    // If MTCs are disabled, the load should fail.
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  } else {
    // Load from server 1 should fail since the cert SAN is not allowed by the
    // permitted_dns_names constraint.
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok2.GetURL(kHostname2, "/simple.html")));
  if (!expect_test_mtc_is_used()) {
    // If MTCs are disabled, the load should fail.
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  } else {
    // Load from server 2 should succeed since the cert SAN is allowed by the
    // permitted_dns_names constraint.
    EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticatedState(
        chrome_test_utils::GetActiveWebContents(this),
        ssl_test_util::AuthState::NONE);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok3.GetURL(kHostname3, "/simple.html")));
  if (!expect_test_mtc_is_used()) {
    // If MTCs are disabled, the load should fail.
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  } else {
    // Load from server 3 should fail since the MTC index is not allowed by the
    // index_not_after constraint.
    EXPECT_NE(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    ssl_test_util::CheckAuthenticationBrokenState(
        chrome_test_utils::GetActiveWebContents(this),
        net::CERT_STATUS_AUTHORITY_INVALID,
        ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
  }
}

class PKIMetadataComponentChromeRootStoreUpdateQwacTest
    : public PKIMetadataComponentChromeRootStoreUpdateTest,
      public testing::WithParamInterface<bool> {
 public:
  PKIMetadataComponentChromeRootStoreUpdateQwacTest() {
    if (GetParam()) {
      feature_list_.InitAndEnableFeature(net::features::kVerifyQWACs);
    } else {
      feature_list_.InitAndDisableFeature(net::features::kVerifyQWACs);
    }
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

INSTANTIATE_TEST_SUITE_P(,
                         PKIMetadataComponentChromeRootStoreUpdateQwacTest,
                         testing::Bool());

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentChromeRootStoreUpdateQwacTest,
                       CheckCrsEutlUpdate) {
  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig server_config;
  server_config.dns_names = {"*.example.com"};
  // Set policy OIDs and QWAC QC types on the leaf so that it will validate as
  // a QWAC. Also include an intermediate so we can set the intermediate as
  // part of the EUTL trust store in the CRS update.
  // OIDs: CABF OV, ETSI QNCP-w
  server_config.policy_oids = {"2.23.140.1.2.2", "0.4.0.194112.1.5"};
  server_config.qwac_qc_types = {bssl::der::Input(net::kEtsiQctWebOid)};
  server_config.intermediate =
      net::EmbeddedTestServer::IntermediateType::kInHandshake;
  https_server_ok.SetSSLConfig(server_config);
  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");

  // Install only the root cert as a trust anchor in CRS and check that the
  // page load is successful but the cert is not a valid QWAC.
  net::TestRootCerts::GetInstance()->Clear();
  int64_t crs_version = net::CompiledChromeRootStoreVersion();
  {
    scoped_refptr<net::X509Certificate> root_cert =
        net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
    ASSERT_TRUE(root_cert);
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    auto* trust_anchor = root_store_proto.add_trust_anchors();
    trust_anchor->set_der(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
    InstallCRSUpdate(root_store_proto);
  }

  ASSERT_TRUE(https_server_ok.Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("a.example.com", "/simple.html")));

  // Check that the page's cert status is not a QWAC.
  content::WebContents* tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE);
  content::NavigationEntry* entry = tab->GetController().GetVisibleEntry();
  net::CertStatus cert_status = entry->GetSSL().cert_status;
  EXPECT_FALSE(cert_status & net::CERT_STATUS_IS_QWAC);

  // Install CRS update that has the root as a trust anchor in CRS and the
  // intermediate as a QWAC issuer.
  {
    scoped_refptr<net::X509Certificate> root_cert =
        net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
    ASSERT_TRUE(root_cert);
    scoped_refptr<net::X509Certificate> intermediate_cert =
        https_server_ok.GetGeneratedIntermediate();
    ASSERT_TRUE(intermediate_cert);

    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    auto* trust_anchor = root_store_proto.add_trust_anchors();
    trust_anchor->set_der(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
    auto* additional_cert = root_store_proto.add_additional_certs();
    additional_cert->set_der(net::x509_util::CryptoBufferAsStringPiece(
        intermediate_cert->cert_buffer()));
    additional_cert->set_eutl(true);
    InstallCRSUpdate(root_store_proto);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("b.example.com", "/simple.html")));

  // Check the page's cert status is a QWAC (if net::features::kVerifyQWACs is
  // enabled).
  tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE);
  cert_status = tab->GetController().GetVisibleEntry()->GetSSL().cert_status;
  EXPECT_EQ(GetParam(), !!(cert_status & net::CERT_STATUS_IS_QWAC));

  // Install a CRS update that has the root as both a trust anchor in CRS and
  // a QWAC issuer
  {
    scoped_refptr<net::X509Certificate> root_cert =
        net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
    ASSERT_TRUE(root_cert);
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    auto* trust_anchor = root_store_proto.add_trust_anchors();
    trust_anchor->set_der(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
    trust_anchor->set_eutl(true);
    InstallCRSUpdate(root_store_proto);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("c.example.com", "/simple.html")));

  // Check the page's cert status is a QWAC (if net::features::kVerifyQWACs is
  // enabled).
  tab = chrome_test_utils::GetActiveWebContents(this);
  ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame()));
  EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
  ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE);
  cert_status = tab->GetController().GetVisibleEntry()->GetSSL().cert_status;
  EXPECT_EQ(GetParam(), !!(cert_status & net::CERT_STATUS_IS_QWAC));
}

// Base test suite for tests that depend on both Certificate Transparency and
// Chrome Root Store updates.
class PKIMetadataComponentCtAndCrsTestBase
    : public InProcessBrowserTest,
      public PKIMetadataComponentInstallerService::Observer {
 public:
  void SetUpInProcessBrowserTestFixture() override {
    PKIMetadataComponentInstallerService::GetInstance()->AddObserver(this);
    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
    ASSERT_TRUE(component_dir_.CreateUniqueTempDir());
    host_resolver()->AddRule("*", "127.0.0.1");
  }

  void TearDownInProcessBrowserTestFixture() override {
    PKIMetadataComponentInstallerService::GetInstance()->RemoveObserver(this);
  }

 protected:
  // Waits for the CT log lists to have been configured at least
  // |expected_times|.
  void WaitForCtConfiguration(int expected_times,
                              bool ct_disabled_by_feature = false) {
    if (ct_disabled_by_feature) {
      // When CT is disabled by the feature flag there are no callbacks to
      // wait on, so just spin the runloop.
      base::RunLoop().RunUntilIdle();
      EXPECT_EQ(ct_log_list_configured_times_, 0);
    } else {
      expected_ct_log_list_configured_times_ = expected_times;
      if (ct_log_list_configured_times_ >=
          expected_ct_log_list_configured_times_) {
        return;
      }
      base::RunLoop run_loop;
      pki_metadata_config_closure_ = run_loop.QuitClosure();
      run_loop.Run();
    }
  }

  const base::FilePath& GetComponentDirPath() const {
    return component_dir_.GetPath();
  }

  void InstallCRSUpdate(chrome_root_store::RootStore root_store_proto) {
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(
          PKIMetadataComponentInstallerService::GetInstance()
              ->WriteCRSDataForTesting(component_dir_.GetPath(),
                                       root_store_proto.SerializeAsString()));
    }

    CRSWaiter waiter(this);
    PKIMetadataComponentInstallerService::GetInstance()
        ->ConfigureChromeRootStore();
    waiter.Wait();
  }

  base::test::ScopedFeatureList scoped_feature_list_;
  base::ScopedTempDir component_dir_;

 private:
  void OnCTLogListConfigured() override {
    ++ct_log_list_configured_times_;
    if (pki_metadata_config_closure_ &&
        ct_log_list_configured_times_ >=
            expected_ct_log_list_configured_times_) {
      std::move(pki_metadata_config_closure_).Run();
    }
  }

  void OnChromeRootStoreConfigured() override {
    if (crs_config_closure_) {
      std::move(crs_config_closure_).Run();
    }
  }

  class CRSWaiter {
   public:
    explicit CRSWaiter(PKIMetadataComponentCtAndCrsTestBase* test) {
      test_ = test;
      test_->crs_config_closure_ = run_loop_.QuitClosure();
    }
    void Wait() { run_loop_.Run(); }

   private:
    base::RunLoop run_loop_;
    raw_ptr<PKIMetadataComponentCtAndCrsTestBase> test_;
  };

  base::OnceClosure pki_metadata_config_closure_;
  int expected_ct_log_list_configured_times_ = 0;
  int ct_log_list_configured_times_ = 0;
  base::OnceClosure crs_config_closure_;
};

class PKIMetadataComponentSctNotAfter
    : public PKIMetadataComponentCtAndCrsTestBase {
 public:
  PKIMetadataComponentSctNotAfter() {
    scoped_feature_list_.InitWithFeatures(
        /*enabled_features=*/
        {features::kCertificateTransparencyAskBeforeEnabling,
#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
         net::features::kChromeRootStoreUsed
#endif
        },
        /*disabled_features=*/{});
  }
};

IN_PROC_BROWSER_TEST_F(PKIMetadataComponentSctNotAfter,
                       TestCRSConstraintsWithStaleCTList) {
  const base::Time kLogStart = base::Time::Now() - base::Days(1);
  const base::Time kLogEnd = base::Time::Now() + base::Days(1);
  CTLog log1("log operator 1", kLogStart, kLogEnd,
             chrome_browser_certificate_transparency::CTLog::RFC6962);

  // Start a test server that uses a certificate with no SCTs
  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig server_config;
  server_config.dns_names = {"*.example.com"};
  https_server_ok.SetSSLConfig(server_config);

  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(https_server_ok.Start());

  // Clear test roots so that cert validation only happens with
  // what's in Chrome Root Store.
  net::TestRootCerts::GetInstance()->Clear();

  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  int64_t crs_version = net::CompiledChromeRootStoreVersion();

  // Install CT configuration that trusts log1, but is stale and so should not
  // be used.
  chrome_browser_certificate_transparency::CTConfig ct_config;
  ct_config.mutable_log_list()->mutable_timestamp()->set_seconds(
      SecondsSinceEpoch(base::Time::Now() - base::Days(300)));
  AddLogToCTConfig(&ct_config, log1);
  // Explicitly allow a stale update to override a newer update.
  PKIMetadataComponentInstallerService::GetInstance()
      ->AllowOldCTUpdateForTesting(true);

  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance()
                    ->WriteCTDataForTesting(GetComponentDirPath(),
                                            ct_config.SerializeAsString()));
  }

  // Install CRS update that trusts root with a SCTNotAfter constraint.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->add_constraints()->set_sct_not_after_sec(
        SecondsSinceEpoch(base::Time::Now() - base::Minutes(20)));

    InstallCRSUpdate(root_store_proto);
  }

  PKIMetadataComponentInstallerService::GetInstance()
      ->ReconfigureAfterNetworkRestart();
  WaitForCtConfiguration(1);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("c.example.com", "/simple.html")));
  // Should be trusted because CT log list is stale, and so SCTs aren't checked
  // for either CT policy or for SCTNotAfter root constraints.
  EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
}

// Test suite for tests that depend on both Certificate Transparency and Chrome
// Root Store updates.
class PKIMetadataComponentCtAndCrsUpdaterTest
    : public PKIMetadataComponentCtAndCrsTestBase,
      public testing::WithParamInterface<CTEnforcement> {
 public:
  PKIMetadataComponentCtAndCrsUpdaterTest() {
    if (GetParam() == CTEnforcement::kDisabledByFeature) {
      scoped_feature_list_.InitWithFeatures(
          /*enabled_features=*/
          {
#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
              net::features::kChromeRootStoreUsed
#endif
          },
          /*disabled_features=*/{
              features::kCertificateTransparencyAskBeforeEnabling});
    } else {
      scoped_feature_list_.InitWithFeatures(
          /*enabled_features=*/
          {features::kCertificateTransparencyAskBeforeEnabling,
#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
           net::features::kChromeRootStoreUsed
#endif
          },
          /*disabled_features=*/{});
    }
  }

 protected:
  void WaitForCtConfiguration(int expected_times) {
    PKIMetadataComponentCtAndCrsTestBase::WaitForCtConfiguration(
        expected_times, GetParam() == CTEnforcement::kDisabledByFeature);
  }
};

IN_PROC_BROWSER_TEST_P(PKIMetadataComponentCtAndCrsUpdaterTest,
                       TestChromeRootStoreConstraintsSct) {
  const base::Time kLogStart = base::Time::Now() - base::Days(1);
  const base::Time kLogEnd = base::Time::Now() + base::Days(1);
  CTLog log1("log operator 1", kLogStart, kLogEnd,
             chrome_browser_certificate_transparency::CTLog::RFC6962);
  CTLog log2(
      "log operator 2", kLogStart, kLogEnd,
      chrome_browser_certificate_transparency::CTLog::LOG_TYPE_UNSPECIFIED);
  CTLog unknown_log(
      "unknown log operator", kLogStart, kLogEnd,
      chrome_browser_certificate_transparency::CTLog::LOG_TYPE_UNSPECIFIED);

  const base::Time kSctTime0UnknownLog = base::Time::Now() - base::Minutes(30);
  const base::Time kSctTime1 = base::Time::Now() - base::Minutes(20);
  const base::Time kSctTime2 = base::Time::Now() - base::Minutes(10);

  // Start a test server that uses a certificate with SCTs for the above test
  // logs.
  net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS);
  net::EmbeddedTestServer::ServerCertificateConfig server_config;
  server_config.dns_names = {"*.example.com"};
  server_config.embedded_scts.emplace_back(log1.id(), log1.key(), kSctTime1);
  server_config.embedded_scts.emplace_back(log2.id(), log2.key(), kSctTime2);
  server_config.embedded_scts.emplace_back(unknown_log.id(), unknown_log.key(),
                                           kSctTime0UnknownLog);
  https_server_ok.SetSSLConfig(server_config);

  https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data");
  ASSERT_TRUE(https_server_ok.Start());

  // Clear test roots so that cert validation only happens with
  // what's in Chrome Root Store.
  net::TestRootCerts::GetInstance()->Clear();

  scoped_refptr<net::X509Certificate> root_cert =
      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
  ASSERT_TRUE(root_cert);
  int64_t crs_version = net::CompiledChromeRootStoreVersion();

  // Install CRS update that trusts root without constraints.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));

    InstallCRSUpdate(root_store_proto);
  }

  // Install CT configuration that trusts log1 and log2.
  //
  // Set up a configuration that will enable or disable CT enforcement
  // depending on the test parameter.
  chrome_browser_certificate_transparency::CTConfig ct_config;
  ct_config.set_disable_ct_enforcement(GetParam() ==
                                       CTEnforcement::kDisabledByProto);
  ct_config.mutable_log_list()->mutable_timestamp()->set_seconds(
      SecondsSinceEpoch(base::Time::Now()));
  AddLogToCTConfig(&ct_config, log1);
  AddLogToCTConfig(&ct_config, log2);

  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance()
                    ->WriteCTDataForTesting(GetComponentDirPath(),
                                            ct_config.SerializeAsString()));
  }

  PKIMetadataComponentInstallerService::GetInstance()
      ->ReconfigureAfterNetworkRestart();
  WaitForCtConfiguration(1);

  // Should be trusted.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("b.example.com", "/simple.html")));
  EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());

  // Install CRS update that trusts root with a SCTNotAfter constraint.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->add_constraints()->set_sct_not_after_sec(
        SecondsSinceEpoch(kSctTime1 + base::Seconds(1)));

    InstallCRSUpdate(root_store_proto);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("c.example.com", "/simple.html")));
  // Should be trusted if CT is enabled since the SCTNotAfter constraint is
  // satisfied by the SCT from log1. Should be trusted if CT feature is
  // disabled since SCTNotAfter fails open when CT is disabled.
  EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());

  // Install CRS update that trusts root with a SCTNotAfter constraint that is
  // before both of the valid SCTs.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->add_constraints()->set_sct_not_after_sec(
        SecondsSinceEpoch(kSctTime0UnknownLog + base::Seconds(1)));

    InstallCRSUpdate(root_store_proto);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("c.example.com", "/simple.html")));
  switch (GetParam()) {
    case CTEnforcement::kEnabled:
    case CTEnforcement::kEnabledWithOne6962Enforcement:
      // Should be distrusted if CT is enabled. The SCTNotAfter constraint is
      // not satisfied by any valid SCT. The SCT from the unknown log is not
      // counted even though the timestamp matches the constraint.
      EXPECT_NE(u"OK",
                chrome_test_utils::GetActiveWebContents(this)->GetTitle());
      break;
    case CTEnforcement::kDisabledByProto:
    case CTEnforcement::kDisabledByFeature:
      // Should be trusted if CT feature is disabled since SCTNotAfter fails
      // open when CT is disabled.
      EXPECT_EQ(u"OK",
                chrome_test_utils::GetActiveWebContents(this)->GetTitle());
      break;
  }

  // Install CRS update that trusts root with a SCTAllAfter constraint that is
  // before both of the valid SCTs.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->add_constraints()->set_sct_all_after_sec(
        SecondsSinceEpoch(kSctTime1 - base::Seconds(1)));

    InstallCRSUpdate(root_store_proto);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("c.example.com", "/simple.html")));
  // Should be trusted if CT is enabled since the SCTAlltAfter constraint is
  // satisfied by the SCT from both logs.
  // Should be trusted if CT feature is disabled since SCTAllAfter fails
  // open when CT is disabled.
  EXPECT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());

  // Install CRS update that trusts root with a SCTAllAfter constraint that is
  // before one of the SCTs but after the other.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(
        net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())));
    anchor->add_constraints()->set_sct_all_after_sec(
        SecondsSinceEpoch(kSctTime1 + base::Seconds(1)));

    InstallCRSUpdate(root_store_proto);
  }

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_server_ok.GetURL("c.example.com", "/simple.html")));
  switch (GetParam()) {
    case CTEnforcement::kEnabled:
    case CTEnforcement::kEnabledWithOne6962Enforcement:
      // Should be distrusted since one of the SCTs was before the SCTAllAfter
      // constraint.
      EXPECT_NE(u"OK",
                chrome_test_utils::GetActiveWebContents(this)->GetTitle());
      break;
    case CTEnforcement::kDisabledByProto:
    case CTEnforcement::kDisabledByFeature:
      // Should be trusted if CT feature is disabled since SCTAllAfter fails
      // open when CT is disabled.
      EXPECT_EQ(u"OK",
                chrome_test_utils::GetActiveWebContents(this)->GetTitle());
      break;
  }
}

INSTANTIATE_TEST_SUITE_P(
    PKIMetadataComponentUpdater,
    PKIMetadataComponentCtAndCrsUpdaterTest,
    testing::Values(CTEnforcement::kEnabled,
                    CTEnforcement::kEnabledWithOne6962Enforcement,
                    CTEnforcement::kDisabledByProto,
                    CTEnforcement::kDisabledByFeature));

class TestDnsOverHttpsConfigSource : public DnsOverHttpsConfigSource {
 public:
  TestDnsOverHttpsConfigSource(std::string dns_over_https_templates,
                               std::string dns_over_https_mode)
      : dns_over_https_templates_(std::move(dns_over_https_templates)),
        dns_over_https_mode_(std::move(dns_over_https_mode)) {}

  TestDnsOverHttpsConfigSource(const TestDnsOverHttpsConfigSource&) = delete;
  TestDnsOverHttpsConfigSource& operator=(const TestDnsOverHttpsConfigSource&) =
      delete;
  ~TestDnsOverHttpsConfigSource() override = default;

  // DnsOverHttpsConfigSource:
  std::string GetDnsOverHttpsMode() const override {
    return dns_over_https_mode_;
  }
  std::string GetDnsOverHttpsTemplates() const override {
    return dns_over_https_templates_;
  }
  bool AutomaticModeFallbackToDohEnabled() const override { return false; }
  bool IsConfigManaged() const override {
    // Return managed=true, otherwise the test config will be ignored if the
    // test is run on an enterprise enrolled device.
    return true;
  }
  void SetDohChangeCallback(base::RepeatingClosure callback) override {}

 private:
  std::string dns_over_https_templates_;
  std::string dns_over_https_mode_;
};

// Test fixture for testing Trust Anchor IDs, including a test DoH server for
// advertising Trust Anchor IDs in DNS.
class PKIMetadataComponentChromeRootStoreUpdateWithDoHServerTest
    : public PKIMetadataComponentChromeRootStoreUpdateTest {
 public:
  static constexpr std::string_view kDohServerHostname = "doh.test";
  static constexpr std::string_view kHostname = "a.com";

  PKIMetadataComponentChromeRootStoreUpdateWithDoHServerTest()
      : PKIMetadataComponentChromeRootStoreUpdateTest() {
    feature_list_.InitAndEnableFeature(net::features::kNonMtcTrustAnchorIDs);
  }

  void SetUpOnMainThread() override {
    // Set up an HTTPS server that has two certificate chains.
    // The first is directly issued by a unique root with the trust anchor ID
    // `kIntermediateTrustAnchorId`.
    // The second chain has a leaf and intermediate issued by the default test
    // root cert, and has no trust anchor ID.
    net::SSLServerConfig server_config;
    server_config.client_hello_callback_for_testing =
        base::BindRepeating(&LogClientHelloTrustAnchorIDs);

    net::EmbeddedTestServer::ServerCertificateConfig tai_cert_config;
    tai_cert_config.intermediate =
        net::EmbeddedTestServer::IntermediateType::kNone;
    tai_cert_config.root = net::EmbeddedTestServer::RootType::kUniqueRoot;
    tai_cert_config.trust_anchor_id =
        base::ToVector(kIntermediateTrustAnchorId);
    tai_cert_config.dns_names.emplace_back(kHostname);

    net::EmbeddedTestServer::ServerCertificateConfig default_cert_config;
    default_cert_config.intermediate =
        net::EmbeddedTestServer::IntermediateType::kInHandshake;
    default_cert_config.root = net::EmbeddedTestServer::RootType::kUniqueRoot;
    default_cert_config.dns_names.emplace_back(kHostname);

    trust_anchor_ids_server_.SetSSLConfig(
        {tai_cert_config, default_cert_config}, server_config);
    trust_anchor_ids_server_.ServeFilesFromSourceDirectory("chrome/test/data");
    ASSERT_TRUE(trust_anchor_ids_server_.Start());

    // Start a DoH server, which ensures we use a resolver with HTTPS RR
    // support. Configure it to serve records for `trust_anchor_ids_server_`.
    doh_server_.SetHostname(kDohServerHostname);
    url::SchemeHostPort tai_host(
        trust_anchor_ids_server_.GetURL(kHostname, "/"));
    doh_server_.AddAddressRecord(tai_host.host(),
                                 net::IPAddress::IPv4Localhost());
    doh_server_.AddRecord(net::BuildTestHttpsServiceRecord(
        net::dns_util::GetNameForHttpsQuery(tai_host),
        /*priority=*/1, /*service_name=*/tai_host.host(),
        {net::BuildTestHttpsServiceTrustAnchorIDsParam(
            GetTrustAnchorIDsForDns())}));
    ASSERT_TRUE(doh_server_.Start());

    doh_config_source_ = std::make_unique<TestDnsOverHttpsConfigSource>(
        doh_server_.GetTemplate(), SecureDnsConfig::kModeSecure);
    SystemNetworkContextManager::GetStubResolverConfigReader()
        ->SetOverrideDnsOverHttpsConfigSource(std::move(doh_config_source_));
    // The net stack doesn't enable DoH when it can't find a system DNS config
    // (see https://crbug.com/40198483).
    SetReplaceSystemDnsConfig();

    // Ensure that the DoH configuration is picked up.
    content::FlushNetworkServiceInstanceForTesting();

    // Add a single bootstrapping rule so we can resolve the DoH server.
    host_resolver()->AddRule(kDohServerHostname, "127.0.0.1");
  }

 protected:
  // The Trust Anchor ID configured by `trust_anchor_ids_server_` for the
  // intermediate that it uses in its certificate chain.
  static constexpr uint8_t kIntermediateTrustAnchorId[] = {0x01, 0x02, 0x03};

  // A Trust Anchor ID that is advertised for `trust_anchor_ids_server_` in DNS,
  // but not actually associated with a certificate chain configured on the
  // server.
  static constexpr uint8_t kAdvertisedButNotServedTrustAnchorId[] = {0x04, 0x05,
                                                                     0x06};
  // A Trust Anchor ID that is neither advertised for `trust_anchor_ids_server_`
  // in DNS, nor actually associated with a certificate chain configured on the
  // server.
  static constexpr uint8_t kNotAdvertisedAndNotServedTrustAnchorId[] = {
      0x07, 0x08, 0x09};

  static constexpr size_t kTaiCredentialNum = 0;
  static constexpr size_t kDefaultCredentialNum = 1;

  // By default, `kIntermediateTrustAnchorId` and
  // `kAdvertisedButNotServedTrustAnchorId` are advertised for `kHostname` in an
  // HTTPS record served by `doh_server_`. Subclasses can override this method
  // to change which Trust Anchor IDs are advertised for this host.
  virtual std::vector<std::vector<uint8_t>> GetTrustAnchorIDsForDns() {
    return {base::ToVector(kAdvertisedButNotServedTrustAnchorId),
            base::ToVector(kIntermediateTrustAnchorId)};
  }

  // Installs a navigation throttle that expects `certificate` to be the served
  // certificate chain on successful responses. Overwrites previous calls to
  // this method (i.e., only one certificate-checking throttle is in place at a
  // time). When the navigation is finished and the inserted throttle is
  // destroyed, UpdateNumObservedResponses() will be called, which allows tests
  // to check that the throttle was successfully installed and observed a
  // navigation.
  void SetExpectedCertificateOnResponses(
      scoped_refptr<net::X509Certificate> certificate) {
    certificate_observer_.InsertThrottleExpectingCertificate(
        chrome_test_utils::GetActiveWebContents(this), certificate);
  }

  // Checks that the most recently installed navigation throttle observed at
  // least one response.
  void CheckThrottleObservedNavigation() {
    ASSERT_GT(certificate_observer_.num_observed_responses(), 0u);
  }

  net::EmbeddedTestServer trust_anchor_ids_server_{
      net::EmbeddedTestServer::TYPE_HTTPS};
  net::TestDohServer doh_server_;

 private:
  base::test::ScopedFeatureList feature_list_;
  std::unique_ptr<TestDnsOverHttpsConfigSource> doh_config_source_;
  CertificateCheckingThrottleController certificate_observer_;
};

IN_PROC_BROWSER_TEST_F(
    PKIMetadataComponentChromeRootStoreUpdateWithDoHServerTest,
    TrustAnchorIDs) {
  int64_t crs_version = net::CompiledChromeRootStoreVersion();

  {
    // Install CRS update that contains only the default root and no trust
    // anchor ids.
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(net::x509_util::CryptoBufferAsStringPiece(
        trust_anchor_ids_server_.GetRoot(kDefaultCredentialNum)
            ->cert_buffer())));

    InstallCRSUpdate(root_store_proto);

    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();

    // Before updating the root store with trust anchor IDs, the server should
    // serve the default credential which has both a leaf and an intermediate.
    scoped_refptr<net::X509Certificate> server_certificate =
        trust_anchor_ids_server_.GetCertificate(kDefaultCredentialNum);
    ASSERT_EQ(server_certificate->intermediate_buffers().size(), 1u);

    SetExpectedCertificateOnResponses(server_certificate);

    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), trust_anchor_ids_server_.GetURL(kHostname, "/simple.html")));
    ASSERT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK");
    CheckThrottleObservedNavigation();
  }

  // Install CRS update that contains two trusted Trust Anchor IDs, including
  // one that is advertised by the server corresponding to its root
  // certificate.
  {
    chrome_root_store::RootStore root_store_proto;
    root_store_proto.set_version_major(++crs_version);
    chrome_root_store::TrustAnchor* anchor =
        root_store_proto.add_trust_anchors();
    anchor->set_der(std::string(net::x509_util::CryptoBufferAsStringPiece(
        trust_anchor_ids_server_.GetRoot(kDefaultCredentialNum)
            ->cert_buffer())));

    chrome_root_store::TrustAnchor* additional_cert1 =
        root_store_proto.add_additional_certs();
    additional_cert1->set_der(
        std::string(net::x509_util::CryptoBufferAsStringPiece(
            trust_anchor_ids_server_.GetRoot(kTaiCredentialNum)
                ->cert_buffer())));
    additional_cert1->set_trust_anchor_id(
        base::as_string_view(kIntermediateTrustAnchorId));
    additional_cert1->set_tls_trust_anchor(true);

    chrome_root_store::TrustAnchor* additional_cert2 =
        root_store_proto.add_additional_certs();
    scoped_refptr<net::X509Certificate> unused_intermediate =
        net::ImportCertFromFile(net::GetTestCertsDirectory(),
                                "verisign_intermediate_ca_2016.pem");
    additional_cert2->set_der(
        std::string(net::x509_util::CryptoBufferAsStringPiece(
            unused_intermediate->cert_buffer())));
    additional_cert2->set_trust_anchor_id(
        base::as_string_view(kNotAdvertisedAndNotServedTrustAnchorId));
    additional_cert2->set_tls_trust_anchor(true);

    InstallCRSUpdate(root_store_proto);

    // Ensure that SSLConfigClients have been notified of the new trust anchor
    // IDs.
    SystemNetworkContextManager::GetInstance()
        ->FlushSSLConfigManagerForTesting();

    // The server should now serve a single leaf, without any intermediates,
    // because the client should signal that it trusts the intermediate as a
    // trust anchor.
    scoped_refptr<net::X509Certificate> server_certificate =
        trust_anchor_ids_server_.GetCertificate(kTaiCredentialNum);
    ASSERT_EQ(server_certificate->intermediate_buffers().size(), 0u);
    SetExpectedCertificateOnResponses(server_certificate);

    // TODO(crbug.com/431064813): remove after debugging test flake.
    LOG(ERROR) << "Beginning navigation with Trust Anchor IDs";

    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), trust_anchor_ids_server_.GetURL(kHostname, "/simple.html")));
    ASSERT_EQ(u"OK", chrome_test_utils::GetActiveWebContents(this)->GetTitle());
    CheckThrottleObservedNavigation();
  }
}


// TODO(crbug.com/40816087) additional Chrome Root Store browser tests to
// add:
//
// * Test that AIA fetching still works after updating CRS.
#endif  // BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)

}  // namespace component_updater
