Cert Management UI V2: queue cert verify requests on DB read

Change the cert verifier service to wait for user settings read from
the ServerCertificateDatabase before running any cert verify requests.
Requests received before the user settings are passed to the cert
verifier are queued and run once the verifier processes the user
settings.

Change-Id: If25f833ceb6e3b3343fffa334b2d83028aae735a
Bug: 40928765
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5857718
Reviewed-by: Dominic Farolino <[email protected]>
Reviewed-by: Matt Mueller <[email protected]>
Commit-Queue: Hubert Chao <[email protected]>
Reviewed-by: Charlie Harrison <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1363753}
diff --git a/chrome/browser/net/DEPS b/chrome/browser/net/DEPS
index d6b451d1..7fda3bc 100644
--- a/chrome/browser/net/DEPS
+++ b/chrome/browser/net/DEPS
@@ -3,4 +3,7 @@
     "+services/network/test",
     "+third_party/boringssl/src/pki",
   ],
+  'server_certificate_database*': [
+    "+third_party/boringssl/src/pki",
+  ],
 }
diff --git a/chrome/browser/net/cert_verifier_policy_browsertest.cc b/chrome/browser/net/cert_verifier_policy_browsertest.cc
index 490eb39c..20db4de79 100644
--- a/chrome/browser/net/cert_verifier_policy_browsertest.cc
+++ b/chrome/browser/net/cert_verifier_policy_browsertest.cc
@@ -41,6 +41,24 @@
 #include "components/onc/onc_constants.h"  // nogncheck
 #endif
 
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+#include "base/containers/span.h"
+#include "base/containers/to_vector.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/net/profile_network_context_service.h"
+#include "chrome/browser/net/profile_network_context_service_factory.h"
+#include "chrome/browser/net/server_certificate_database.h"     // nogncheck
+#include "chrome/browser/net/server_certificate_database.pb.h"  // nogncheck
+#include "chrome/browser/net/server_certificate_database_service.h"  // nogncheck
+#include "chrome/browser/net/server_certificate_database_service_factory.h"  // nogncheck
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "crypto/sha2.h"
+#endif  // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+
 // Testing the CACertificates policy
 class CertVerifierServiceCACertificatesPolicyTest
     : public policy::PolicyTest,
@@ -854,3 +872,114 @@
                          CertVerifierServiceNewAndOncCertificatePoliciesTest,
                          ::testing::Bool());
 #endif  // BUILDFLAG(IS_CHROMEOS)
+
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+// Tests that when certificates are simultaneously added to both the user
+// added certs database and the new CACertificates/CAHintCertificates policies,
+// that they are both honored.
+class CertVerifierServicePolicyAndUserRootsTest
+    : public policy::PolicyTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  CertVerifierServicePolicyAndUserRootsTest() {
+    feature_list_.InitWithFeatures({features::kEnableCertManagementUIV2,
+                                    features::kEnableCertManagementUIV2Write},
+                                   {});
+  }
+  bool add_certs() const { return GetParam(); }
+
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(CertVerifierServicePolicyAndUserRootsTest,
+                       UserRootsAndPolicyCombined) {
+  net::EmbeddedTestServer test_server_for_user_added{
+      net::EmbeddedTestServer::TYPE_HTTPS};
+  net::EmbeddedTestServer test_server_for_policy{
+      net::EmbeddedTestServer::TYPE_HTTPS};
+  // Configure both test servers to serve chains with unique roots and with
+  // an intermediate that is not sent by the testserver nor available via
+  // AIA. Neither server should be trusted unless its intermediate is
+  // supplied as a hint via policy and its root is trusted via policy.
+  net::EmbeddedTestServer::ServerCertificateConfig test_cert_config;
+  test_cert_config.intermediate =
+      net::EmbeddedTestServer::IntermediateType::kMissing;
+  test_cert_config.root = net::EmbeddedTestServer::RootType::kUniqueRoot;
+  test_server_for_user_added.SetSSLConfig(test_cert_config);
+  test_server_for_policy.SetSSLConfig(test_cert_config);
+  test_server_for_user_added.ServeFilesFromSourceDirectory("chrome/test/data");
+  test_server_for_policy.ServeFilesFromSourceDirectory("chrome/test/data");
+
+  ASSERT_TRUE(test_server_for_user_added.Start());
+  ASSERT_TRUE(test_server_for_policy.Start());
+  if (add_certs()) {
+    net::ServerCertificateDatabaseService* server_certificate_database_service =
+        net::ServerCertificateDatabaseServiceFactory::GetForBrowserContext(
+            browser()->profile());
+    {
+      scoped_refptr<net::X509Certificate> root_cert =
+          test_server_for_user_added.GetRoot();
+      net::ServerCertificateDatabase::CertInformation user_root_info;
+      user_root_info.sha256hash_hex =
+          base::HexEncode(crypto::SHA256Hash(root_cert->cert_span()));
+      user_root_info.cert_metadata.mutable_trust()->set_trust_type(
+          chrome_browser_server_certificate_database::CertificateTrust::
+              CERTIFICATE_TRUST_TYPE_TRUSTED);
+      user_root_info.der_cert = base::ToVector(root_cert->cert_span());
+
+      base::test::TestFuture<bool> future;
+      server_certificate_database_service->AddOrUpdateUserCertificate(
+          std::move(user_root_info), future.GetCallback());
+      ASSERT_TRUE(future.Get());
+    }
+    {
+      scoped_refptr<net::X509Certificate> hint_cert =
+          test_server_for_user_added.GetGeneratedIntermediate();
+
+      net::ServerCertificateDatabase::CertInformation user_hint_info;
+      user_hint_info.sha256hash_hex =
+          base::HexEncode(crypto::SHA256Hash(hint_cert->cert_span()));
+      user_hint_info.cert_metadata.mutable_trust()->set_trust_type(
+          chrome_browser_server_certificate_database::CertificateTrust::
+              CERTIFICATE_TRUST_TYPE_UNSPECIFIED);
+      user_hint_info.der_cert = base::ToVector(hint_cert->cert_span());
+
+      base::test::TestFuture<bool> future;
+      server_certificate_database_service->AddOrUpdateUserCertificate(
+          std::move(user_hint_info), future.GetCallback());
+      ASSERT_TRUE(future.Get());
+    }
+
+    auto policy_ca_certs = base::Value::List().Append(
+        base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(
+            test_server_for_policy.GetRoot()->cert_buffer())));
+
+    auto policy_hint_certs = base::Value::List().Append(
+        base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(
+            test_server_for_policy.GetGeneratedIntermediate()->cert_buffer())));
+
+    policy::PolicyMap policies;
+    SetPolicy(&policies, policy::key::kCACertificates,
+              std::make_optional(base::Value(std::move(policy_ca_certs))));
+    SetPolicy(&policies, policy::key::kCAHintCertificates,
+              std::make_optional(base::Value(std::move(policy_hint_certs))));
+    // Policy updates will also trigger an update to the Cert Verifier, pulling
+    // in the certs from ServerCertificateDatabase.
+    UpdateProviderPolicy(policies);
+  }
+
+  ASSERT_TRUE(
+      NavigateToUrl(test_server_for_policy.GetURL("/simple.html"), this));
+  EXPECT_NE(add_certs(), chrome_browser_interstitials::IsShowingInterstitial(
+                             chrome_test_utils::GetActiveWebContents(this)));
+
+  ASSERT_TRUE(
+      NavigateToUrl(test_server_for_user_added.GetURL("/simple.html"), this));
+  EXPECT_NE(add_certs(), chrome_browser_interstitials::IsShowingInterstitial(
+                             chrome_test_utils::GetActiveWebContents(this)));
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         CertVerifierServicePolicyAndUserRootsTest,
+                         ::testing::Bool());
+#endif  // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
diff --git a/chrome/browser/net/cert_verifier_service_browsertest.cc b/chrome/browser/net/cert_verifier_service_browsertest.cc
index 562cbff..dbc95a3 100644
--- a/chrome/browser/net/cert_verifier_service_browsertest.cc
+++ b/chrome/browser/net/cert_verifier_service_browsertest.cc
@@ -29,6 +29,24 @@
 #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+#include "base/containers/span.h"
+#include "base/containers/to_vector.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/net/profile_network_context_service.h"
+#include "chrome/browser/net/profile_network_context_service_factory.h"
+#include "chrome/browser/net/server_certificate_database.h"     // nogncheck
+#include "chrome/browser/net/server_certificate_database.pb.h"  // nogncheck
+#include "chrome/browser/net/server_certificate_database_service.h"  // nogncheck
+#include "chrome/browser/net/server_certificate_database_service_factory.h"  // nogncheck
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "crypto/sha2.h"
+#endif  // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+
 #if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
 class CertVerifierServiceChromeRootStoreOptionalTest
     : public PlatformBrowserTest,
@@ -183,3 +201,168 @@
       GetActiveWebContents()));
 }
 #endif  // BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
+
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+
+class CertVerifierUserSettingsTest : public PlatformBrowserTest {
+ public:
+  CertVerifierUserSettingsTest() {
+    feature_list_.InitWithFeatures({features::kEnableCertManagementUIV2,
+                                    features::kEnableCertManagementUIV2Write},
+                                   {});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(CertVerifierUserSettingsTest, TestUserSettingsUsed) {
+  net::EmbeddedTestServer https_test_server{
+      net::EmbeddedTestServer::TYPE_HTTPS};
+  net::EmbeddedTestServer::ServerCertificateConfig test_cert_config;
+  test_cert_config.intermediate =
+      net::EmbeddedTestServer::IntermediateType::kMissing;
+  test_cert_config.root = net::EmbeddedTestServer::RootType::kUniqueRoot;
+  https_test_server.SetSSLConfig(test_cert_config);
+
+  https_test_server.ServeFilesFromSourceDirectory("chrome/test/data");
+  ASSERT_TRUE(https_test_server.Start());
+
+  ProfileNetworkContextService* profile_network_context_service =
+      ProfileNetworkContextServiceFactory::GetForContext(browser()->profile());
+  net::ServerCertificateDatabaseService* server_certificate_database_service =
+      net::ServerCertificateDatabaseServiceFactory::GetForBrowserContext(
+          browser()->profile());
+
+  {
+    scoped_refptr<net::X509Certificate> root_cert = https_test_server.GetRoot();
+    net::ServerCertificateDatabase::CertInformation user_root_info;
+    user_root_info.sha256hash_hex =
+        base::HexEncode(crypto::SHA256Hash(root_cert->cert_span()));
+    user_root_info.cert_metadata.mutable_trust()->set_trust_type(
+        chrome_browser_server_certificate_database::CertificateTrust::
+            CERTIFICATE_TRUST_TYPE_TRUSTED);
+    user_root_info.der_cert = base::ToVector(root_cert->cert_span());
+
+    base::test::TestFuture<bool> future;
+    server_certificate_database_service->AddOrUpdateUserCertificate(
+        std::move(user_root_info), future.GetCallback());
+    ASSERT_TRUE(future.Get());
+  }
+  {
+    scoped_refptr<net::X509Certificate> hint_cert =
+        https_test_server.GetGeneratedIntermediate();
+    net::ServerCertificateDatabase::CertInformation user_hint_info;
+    user_hint_info.sha256hash_hex =
+        base::HexEncode(crypto::SHA256Hash(hint_cert->cert_span()));
+    user_hint_info.cert_metadata.mutable_trust()->set_trust_type(
+        chrome_browser_server_certificate_database::CertificateTrust::
+            CERTIFICATE_TRUST_TYPE_UNSPECIFIED);
+    user_hint_info.der_cert = base::ToVector(hint_cert->cert_span());
+
+    base::test::TestFuture<bool> future;
+    server_certificate_database_service->AddOrUpdateUserCertificate(
+        std::move(user_hint_info), future.GetCallback());
+    ASSERT_TRUE(future.Get());
+  }
+
+  // TODO(crbug.com/40928765): remove once a notification method auto-runs
+  // this.
+  profile_network_context_service->UpdateAdditionalCertificates();
+  // Clear test roots so that cert validation only happens with
+  // what's in the relevant root store + user settings.
+  net::TestRootCerts::GetInstance()->Clear();
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_test_server.GetURL("/simple.html")));
+  EXPECT_FALSE(chrome_browser_interstitials::IsShowingInterstitial(
+      chrome_test_utils::GetActiveWebContents(this)));
+}
+
+IN_PROC_BROWSER_TEST_F(CertVerifierUserSettingsTest,
+                       TestUserSettingsUsedDistrusted) {
+  net::EmbeddedTestServer https_test_server(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_test_server.SetSSLConfig(
+      net::test_server::EmbeddedTestServer::CERT_AUTO);
+  https_test_server.ServeFilesFromSourceDirectory("chrome/test/data");
+  ASSERT_TRUE(https_test_server.Start());
+  ProfileNetworkContextService* profile_network_context_service =
+      ProfileNetworkContextServiceFactory::GetForContext(browser()->profile());
+  net::ServerCertificateDatabaseService* server_certificate_database_service =
+      net::ServerCertificateDatabaseServiceFactory::GetForBrowserContext(
+          browser()->profile());
+
+  scoped_refptr<net::X509Certificate> root_cert =
+      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
+  ASSERT_TRUE(root_cert);
+
+  net::ServerCertificateDatabase::CertInformation cert_info;
+  cert_info.sha256hash_hex =
+      base::HexEncode(crypto::SHA256Hash(root_cert->cert_span()));
+  cert_info.cert_metadata.mutable_trust()->set_trust_type(
+      chrome_browser_server_certificate_database::CertificateTrust::
+          CERTIFICATE_TRUST_TYPE_DISTRUSTED);
+  cert_info.der_cert = base::ToVector(root_cert->cert_span());
+
+  base::test::TestFuture<bool> future;
+  server_certificate_database_service->AddOrUpdateUserCertificate(
+      std::move(cert_info), future.GetCallback());
+  ASSERT_TRUE(future.Get());
+
+  // TODO(crbug.com/40928765): remove once a notification method auto-runs
+  // this.
+  profile_network_context_service->UpdateAdditionalCertificates();
+
+  // We don't clear test roots; the distrusted addition in the user db should
+  // override the test root trust.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_test_server.GetURL("/simple.html")));
+  EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
+      chrome_test_utils::GetActiveWebContents(this)));
+}
+
+IN_PROC_BROWSER_TEST_F(CertVerifierUserSettingsTest,
+                       TestUserSettingsUsedDistrustedIncognito) {
+  net::EmbeddedTestServer https_test_server(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_test_server.SetSSLConfig(
+      net::test_server::EmbeddedTestServer::CERT_AUTO);
+  https_test_server.ServeFilesFromSourceDirectory("chrome/test/data");
+  ASSERT_TRUE(https_test_server.Start());
+  ProfileNetworkContextService* profile_network_context_service =
+      ProfileNetworkContextServiceFactory::GetForContext(browser()->profile());
+  net::ServerCertificateDatabaseService* server_certificate_database_service =
+      net::ServerCertificateDatabaseServiceFactory::GetForBrowserContext(
+          browser()->profile());
+
+  scoped_refptr<net::X509Certificate> root_cert =
+      net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
+  ASSERT_TRUE(root_cert);
+
+  net::ServerCertificateDatabase::CertInformation cert_info;
+  cert_info.sha256hash_hex =
+      base::HexEncode(crypto::SHA256Hash(root_cert->cert_span()));
+  cert_info.cert_metadata.mutable_trust()->set_trust_type(
+      chrome_browser_server_certificate_database::CertificateTrust::
+          CERTIFICATE_TRUST_TYPE_DISTRUSTED);
+  cert_info.der_cert = base::ToVector(root_cert->cert_span());
+
+  base::test::TestFuture<bool> future;
+  server_certificate_database_service->AddOrUpdateUserCertificate(
+      std::move(cert_info), future.GetCallback());
+  ASSERT_TRUE(future.Get());
+
+  // TODO(crbug.com/40928765): remove once a notification method auto-runs
+  // this.
+  profile_network_context_service->UpdateAdditionalCertificates();
+
+  Browser* incognito_browser = CreateIncognitoBrowser();
+
+  // We don't clear test roots; the distrusted addition in the user db should
+  // override the test root trust, even for incognito.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      incognito_browser, https_test_server.GetURL("/simple.html")));
+  EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
+      incognito_browser->tab_strip_model()->GetActiveWebContents()));
+}
+#endif  // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index f25b1d24..14aec5a 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -12,6 +12,7 @@
 #include "base/check_op.h"
 #include "base/command_line.h"
 #include "base/containers/flat_map.h"
+#include "base/containers/to_vector.h"
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -147,6 +148,13 @@
 #include "components/enterprise/client_certificates/core/features.h"
 #endif
 
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+#include "chrome/browser/net/server_certificate_database.h"     // nogncheck
+#include "chrome/browser/net/server_certificate_database.pb.h"  // nogncheck
+#include "chrome/browser/net/server_certificate_database_service.h"  // nogncheck
+#include "chrome/browser/net/server_certificate_database_service_factory.h"  // nogncheck
+#endif
+
 namespace {
 
 bool* g_discard_domain_reliability_uploads_for_testing = nullptr;
@@ -687,8 +695,8 @@
     std::string_view spki_piece;
     bool success = net::asn1::ExtractSPKIFromDERCert(decoded, &spki_piece);
     if (success) {
-      additional_certificates->distrusted_spkis.emplace_back(spki_piece.begin(),
-                                                             spki_piece.end());
+      additional_certificates->distrusted_spkis.push_back(
+          base::ToVector(base::as_byte_span(spki_piece)));
     }
   }
 
@@ -701,14 +709,87 @@
 }
 
 void ProfileNetworkContextService::UpdateAdditionalCertificates() {
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+  if (base::FeatureList::IsEnabled(features::kEnableCertManagementUIV2Write)) {
+    net::ServerCertificateDatabaseService* cert_db_service =
+        net::ServerCertificateDatabaseServiceFactory::GetForBrowserContext(
+            profile_);
+
+    cert_db_service->GetAllCertificates(
+        base::BindOnce(&ProfileNetworkContextService::
+                           UpdateAdditionalCertificatesWithUserAddedCerts,
+                       base::Unretained(this)));
+  } else {
+    profile_->ForEachLoadedStoragePartition(
+        [&](content::StoragePartition* storage_partition) {
+          storage_partition->GetCertVerifierServiceUpdater()
+              ->UpdateAdditionalCertificates(
+                  GetCertificatePolicy(storage_partition->GetPath()));
+        });
+  }
+#else
   profile_->ForEachLoadedStoragePartition(
       [&](content::StoragePartition* storage_partition) {
         storage_partition->GetCertVerifierServiceUpdater()
             ->UpdateAdditionalCertificates(
                 GetCertificatePolicy(storage_partition->GetPath()));
       });
+#endif
 }
 
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+void ProfileNetworkContextService::
+    UpdateAdditionalCertificatesWithUserAddedCerts(
+        std::vector<net::ServerCertificateDatabase::CertInformation>
+            cert_infos) {
+  profile_->ForEachLoadedStoragePartition(
+      [&](content::StoragePartition* storage_partition) {
+        cert_verifier::mojom::AdditionalCertificatesPtr additional_certs =
+            GetCertificatePolicy(storage_partition->GetPath());
+
+        for (const auto& cert_info : cert_infos) {
+          std::optional<bssl::CertificateTrustType> trust =
+              net::ServerCertificateDatabase::GetUserCertificateTrust(
+                  cert_info);
+          if (!trust) {
+            continue;
+          }
+          switch (trust.value()) {
+            case bssl::CertificateTrustType::UNSPECIFIED:
+              additional_certs->all_certificates.push_back(cert_info.der_cert);
+              break;
+
+            case bssl::CertificateTrustType::DISTRUSTED: {
+              std::string_view spki_piece;
+              bool success = net::asn1::ExtractSPKIFromDERCert(
+                  base::as_string_view(cert_info.der_cert), &spki_piece);
+              if (success) {
+                additional_certs->distrusted_spkis.push_back(
+                    base::ToVector(base::as_byte_span(spki_piece)));
+              }
+              break;
+            }
+
+            case bssl::CertificateTrustType::TRUSTED_ANCHOR:
+              // TODO(crbug.com/40928765): add additional constraints.
+              additional_certs->trust_anchors_with_enforced_constraints
+                  .push_back(cert_info.der_cert);
+              break;
+
+            case bssl::CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
+            case bssl::CertificateTrustType::TRUSTED_LEAF:
+              // TODO(crbug.com/40928765): There's currently no place to pass
+              // this through mojo to the cert verifier; add this to the mojo
+              // message after this becomes possible.
+              continue;
+          }
+        }
+        storage_partition->GetCertVerifierServiceUpdater()
+            ->UpdateAdditionalCertificates(std::move(additional_certs));
+      });
+}
+#endif  // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+
 void ProfileNetworkContextService::ScheduleUpdateCertificatePolicy() {
   cert_policy_update_timer_.Start(
       FROM_HERE, base::Seconds(0), this,
@@ -1272,18 +1353,19 @@
   // TODO(crbug.com/40928765): check to see if IsManaged() ensures the pref
   // isn't set in user profiles, or if that does something else. If that's true,
   // add an isManaged() check here.
-  // TODO(crbug.com/40928765): add async calls to get the User Certs from
-  // server_cert_database_ and then feed it to the CertVerifiers
-  // through the cert_verifier_updater
-  // (storage_partition->GetCertVerifierServiceUpdater()).
-  // verifications need to wait for for these certs to get to the cert verifier.
-  //
-  // Will have to think about if separate mojom call should be used or if the
-  // currently existing one should be repurposed.
-  // Will also have to consider any caching/reuse to reduce amount of DB reads
-  // necessary.
+
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+  if (base::FeatureList::IsEnabled(features::kEnableCertManagementUIV2Write)) {
+    cert_verifier_creation_params->wait_for_update = true;
+    UpdateAdditionalCertificates();
+  } else {
+    cert_verifier_creation_params->initial_additional_certificates =
+        GetCertificatePolicy(GetPartitionPath(relative_partition_path));
+  }
+#else
   cert_verifier_creation_params->initial_additional_certificates =
       GetCertificatePolicy(GetPartitionPath(relative_partition_path));
+#endif  // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
 
 #if BUILDFLAG(IS_CHROMEOS)
   // Disable idle sockets close on memory pressure if configured by finch or
diff --git a/chrome/browser/net/profile_network_context_service.h b/chrome/browser/net/profile_network_context_service.h
index 94d4927..2362065d 100644
--- a/chrome/browser/net/profile_network_context_service.h
+++ b/chrome/browser/net/profile_network_context_service.h
@@ -33,6 +33,10 @@
 #include "services/network/public/mojom/cookie_manager.mojom-forward.h"
 #include "services/network/public/mojom/network_context.mojom-forward.h"
 
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+#include "chrome/browser/net/server_certificate_database.h"  // nogncheck
+#endif
+
 class PrefRegistrySimple;
 class Profile;
 
@@ -80,7 +84,7 @@
           cert_verifier_creation_params);
 
   // Update all of the profile_'s CertVerifierServices with certificates from
-  // enterprise policies.
+  // enterprise policies, and any user-added certificates if present.
   void UpdateAdditionalCertificates();
 
   struct CertificatePoliciesForView {
@@ -175,6 +179,13 @@
   cert_verifier::mojom::AdditionalCertificatesPtr GetCertificatePolicy(
       const base::FilePath& storage_partition_path);
 
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+  // Like UpdateAdditionalCertificates, but also includes the passed in user
+  // added certificates.
+  void UpdateAdditionalCertificatesWithUserAddedCerts(
+      std::vector<net::ServerCertificateDatabase::CertInformation> cert_infos);
+#endif
+
   bool ShouldSplitAuthCacheByNetworkIsolationKey() const;
   void UpdateSplitAuthCacheByNetworkIsolationKey();
 
diff --git a/chrome/browser/net/profile_network_context_service_factory.cc b/chrome/browser/net/profile_network_context_service_factory.cc
index 9a0e9df3..09eaec4 100644
--- a/chrome/browser/net/profile_network_context_service_factory.cc
+++ b/chrome/browser/net/profile_network_context_service_factory.cc
@@ -8,6 +8,7 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/buildflags.h"
 #include "crypto/crypto_buildflags.h"
 
 #if BUILDFLAG(USE_NSS_CERTS)
@@ -18,6 +19,10 @@
 #include "chrome/browser/certificate_provider/certificate_provider_service_factory.h"
 #endif
 
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+#include "chrome/browser/net/server_certificate_database_service_factory.h"  // nogncheck
+#endif
+
 ProfileNetworkContextService*
 ProfileNetworkContextServiceFactory::GetForContext(
     content::BrowserContext* browser_context) {
@@ -54,6 +59,9 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   DependsOn(chromeos::CertificateProviderServiceFactory::GetInstance());
 #endif
+#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
+  DependsOn(net::ServerCertificateDatabaseServiceFactory::GetInstance());
+#endif
 }
 
 ProfileNetworkContextServiceFactory::~ProfileNetworkContextServiceFactory() =
diff --git a/chrome/browser/net/server_certificate_database.cc b/chrome/browser/net/server_certificate_database.cc
index 7dc4991..a9aa1be 100644
--- a/chrome/browser/net/server_certificate_database.cc
+++ b/chrome/browser/net/server_certificate_database.cc
@@ -8,10 +8,12 @@
 #include "base/files/file_path.h"
 #include "base/sequence_checker.h"
 #include "build/build_config.h"
+#include "net/cert/x509_util.h"
 #include "sql/init_status.h"
 #include "sql/meta_table.h"
 #include "sql/statement.h"
 #include "sql/transaction.h"
+#include "third_party/boringssl/src/pki/parsed_certificate.h"
 
 namespace net {
 
@@ -41,7 +43,9 @@
 
 ServerCertificateDatabase::ServerCertificateDatabase(
     const base::FilePath& storage_dir) {
-  InitInternal(storage_dir);
+  auto status = InitInternal(storage_dir);
+
+  CHECK(status == sql::InitStatus::INIT_OK);
 }
 
 ServerCertificateDatabase::~ServerCertificateDatabase() = default;
@@ -108,6 +112,7 @@
       SQL_FROM_HERE,
       "INSERT OR REPLACE INTO certificates(sha256hash_hex, der_cert, "
       "trust_settings) VALUES(?,?,?)"));
+  DCHECK(insert_statement.is_valid());
   insert_statement.BindString(0, cert_info.sha256hash_hex);
   insert_statement.BindBlob(1, cert_info.der_cert);
   insert_statement.BindBlob(2, base::as_byte_span(proto_bytes));
@@ -123,6 +128,7 @@
       "SELECT sha256hash_hex, der_cert, trust_settings FROM certificates";
   sql::Statement statement(
       db_.GetCachedStatement(SQL_FROM_HERE, kSqlSelectAllCerts));
+  DCHECK(statement.is_valid());
   while (statement.Step()) {
     ServerCertificateDatabase::CertInformation cert_info;
     cert_info.sha256hash_hex = statement.ColumnString(0);
@@ -147,4 +153,52 @@
 ServerCertificateDatabase::CertInformation::operator=(
     ServerCertificateDatabase::CertInformation&& other) = default;
 
+std::optional<bssl::CertificateTrustType>
+ServerCertificateDatabase::GetUserCertificateTrust(
+    const net::ServerCertificateDatabase::CertInformation& cert_info) {
+  using chrome_browser_server_certificate_database::CertificateTrust;
+  switch (cert_info.cert_metadata.trust().trust_type()) {
+    case CertificateTrust::CERTIFICATE_TRUST_TYPE_UNSPECIFIED:
+      return bssl::CertificateTrustType::UNSPECIFIED;
+    case CertificateTrust::CERTIFICATE_TRUST_TYPE_DISTRUSTED:
+      return bssl::CertificateTrustType::DISTRUSTED;
+    case CertificateTrust::CERTIFICATE_TRUST_TYPE_TRUSTED: {
+      auto parsed = bssl::ParsedCertificate::Create(
+          net::x509_util::CreateCryptoBuffer(cert_info.der_cert),
+          net::x509_util::DefaultParseCertificateOptions(), nullptr);
+
+      if (!parsed) {
+        return std::nullopt;
+      }
+
+      // If basic constraints are missing, assume that is_ca is set.
+      bool isCA = !parsed->has_basic_constraints() ||
+                  (parsed->has_basic_constraints() &&
+                   parsed->basic_constraints().is_ca);
+
+      bool has_names = parsed->has_subject_alt_names() &&
+                       !(parsed->subject_alt_names()->dns_names.empty() &&
+                         parsed->subject_alt_names()->ip_addresses.empty());
+
+      if (isCA) {
+        if (has_names) {
+          return bssl::CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF;
+        } else {
+          return bssl::CertificateTrustType::TRUSTED_ANCHOR;
+        }
+      }
+
+      // isCA = false
+      if (has_names) {
+        return bssl::CertificateTrustType::TRUSTED_LEAF;
+      } else {
+        return std::nullopt;
+      }
+    }
+    case CertificateTrust::CERTIFICATE_TRUST_TYPE_UNKNOWN:
+    default:
+      return std::nullopt;
+  }
+}
+
 }  // namespace net
diff --git a/chrome/browser/net/server_certificate_database.h b/chrome/browser/net/server_certificate_database.h
index eb2fdf5e..cdc55c8 100644
--- a/chrome/browser/net/server_certificate_database.h
+++ b/chrome/browser/net/server_certificate_database.h
@@ -5,12 +5,15 @@
 #ifndef CHROME_BROWSER_NET_SERVER_CERTIFICATE_DATABASE_H_
 #define CHROME_BROWSER_NET_SERVER_CERTIFICATE_DATABASE_H_
 
+#include <optional>
+
 #include "base/files/file_path.h"
 #include "base/sequence_checker.h"
 #include "base/thread_annotations.h"
 #include "chrome/browser/net/server_certificate_database.pb.h"
 #include "sql/database.h"
 #include "sql/init_status.h"
+#include "third_party/boringssl/src/pki/trust_store.h"
 
 namespace net {
 
@@ -40,6 +43,9 @@
       delete;
   ~ServerCertificateDatabase();
 
+  static std::optional<bssl::CertificateTrustType> GetUserCertificateTrust(
+      const net::ServerCertificateDatabase::CertInformation& cert_info);
+
   // Insert a new certificate into the database, or if the certificate is
   // already present (as indicated by cert_info.sha256hash_hex), update the
   // entry in the database.
diff --git a/chrome/browser/net/server_certificate_database_unittest.cc b/chrome/browser/net/server_certificate_database_unittest.cc
index 50c7729a..41416df0 100644
--- a/chrome/browser/net/server_certificate_database_unittest.cc
+++ b/chrome/browser/net/server_certificate_database_unittest.cc
@@ -123,4 +123,44 @@
                            CertInfoEquals(std::ref(intermediate_cert_info))));
 }
 
+TEST(ServerCertificateDatabaseTrustTest, TestTrustMappings) {
+  auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
+
+  EXPECT_EQ(bssl::CertificateTrustType::UNSPECIFIED,
+            ServerCertificateDatabase::GetUserCertificateTrust(MakeCertInfo(
+                intermediate->GetDER(),
+                CertificateTrust::CERTIFICATE_TRUST_TYPE_UNSPECIFIED)));
+
+  EXPECT_EQ(bssl::CertificateTrustType::DISTRUSTED,
+            ServerCertificateDatabase::GetUserCertificateTrust(MakeCertInfo(
+                root->GetDER(),
+                CertificateTrust::CERTIFICATE_TRUST_TYPE_DISTRUSTED)));
+
+  EXPECT_EQ(bssl::CertificateTrustType::TRUSTED_ANCHOR,
+            ServerCertificateDatabase::GetUserCertificateTrust(MakeCertInfo(
+                intermediate->GetDER(),
+                CertificateTrust::CERTIFICATE_TRUST_TYPE_TRUSTED)));
+
+  EXPECT_EQ(
+      bssl::CertificateTrustType::TRUSTED_LEAF,
+      ServerCertificateDatabase::GetUserCertificateTrust(MakeCertInfo(
+          leaf->GetDER(), CertificateTrust::CERTIFICATE_TRUST_TYPE_TRUSTED)));
+
+  leaf->SetBasicConstraints(/*is_ca=*/true, /*path_len=*/-1);
+  EXPECT_EQ(
+      bssl::CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF,
+      ServerCertificateDatabase::GetUserCertificateTrust(MakeCertInfo(
+          leaf->GetDER(), CertificateTrust::CERTIFICATE_TRUST_TYPE_TRUSTED)));
+
+  EXPECT_EQ(
+      bssl::CertificateTrustType::TRUSTED_ANCHOR,
+      ServerCertificateDatabase::GetUserCertificateTrust(MakeCertInfo(
+          root->GetDER(), CertificateTrust::CERTIFICATE_TRUST_TYPE_TRUSTED)));
+
+  EXPECT_EQ(
+      std::nullopt,
+      ServerCertificateDatabase::GetUserCertificateTrust(MakeCertInfo(
+          "invalidcertder", CertificateTrust::CERTIFICATE_TRUST_TYPE_TRUSTED)));
+}
+
 }  // namespace net