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

#include "net/cert/cert_verify_proc_android.h"

#include <memory>
#include <vector>

#include "base/android/android_info.h"
#include "net/cert/cert_net_fetcher.h"
#include "net/cert/cert_verify_proc_android.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/crl_set.h"
#include "net/cert/internal/test_helpers.h"
#include "net/cert/mock_cert_net_fetcher.h"
#include "net/cert/test_root_certs.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/log/net_log_with_source.h"
#include "net/test/cert_builder.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_certificate_data.h"
#include "net/test/test_data_directory.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

using ::testing::_;
using ::testing::ByMove;
using ::testing::Return;

namespace net {

namespace {

const char kHostname[] = "example.com";
const GURL kRootURL("http://aia.test/root");
const GURL kIntermediateURL("http://aia.test/intermediate");

std::unique_ptr<CertNetFetcher::Request>
CreateMockRequestWithInvalidCertificate() {
  return MockCertNetFetcherRequest::Create(std::vector<uint8_t>({1, 2, 3}));
}

// A test fixture for testing CertVerifyProcAndroid AIA fetching. It creates,
// sets up, and shuts down a MockCertNetFetcher for CertVerifyProcAndroid to
// use, and enables the field trial for AIA fetching.
class CertVerifyProcAndroidTestWithAIAFetching : public testing::Test {
 public:
  void SetUp() override {
    fetcher_ = base::MakeRefCounted<MockCertNetFetcher>();

    // Generate a certificate chain with AIA pointers. Tests can modify these
    // if testing a different scenario.
    std::tie(leaf_, intermediate_, root_) = CertBuilder::CreateSimpleChain3();
    root_->SetCaIssuersUrl(kRootURL);
    intermediate_->SetCaIssuersUrl(kRootURL);
    leaf_->SetCaIssuersUrl(kIntermediateURL);
    leaf_->SetSubjectAltName(kHostname);
  }

  void TearDown() override {
    // Ensure that mock expectations are checked, since the CertNetFetcher is
    // global and leaky.
    ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(fetcher_.get()));
  }

  scoped_refptr<X509Certificate> LeafOnly() {
    return leaf_->GetX509Certificate();
  }

  scoped_refptr<X509Certificate> LeafWithIntermediate() {
    return leaf_->GetX509CertificateChain();
  }

 protected:
  void TrustTestRoot() {
    scoped_test_root_.Reset({root_->GetX509Certificate()});
  }

  scoped_refptr<MockCertNetFetcher> fetcher_;
  std::unique_ptr<CertBuilder> root_;
  std::unique_ptr<CertBuilder> intermediate_;
  std::unique_ptr<CertBuilder> leaf_;

 private:
  ScopedTestRoot scoped_test_root_;
};

}  // namespace

// Tests that if the proper intermediates are supplied in the server-sent chain,
// no AIA fetch occurs.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching,
       NoFetchIfProperIntermediatesSupplied) {
  TrustTestRoot();
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());
  CertVerifyResult verify_result;
  EXPECT_EQ(OK, proc->Verify(LeafWithIntermediate().get(), kHostname,
                             /*ocsp_response=*/std::string(),
                             /*sct_list=*/std::string(), 0, &verify_result,
                             NetLogWithSource()));
}

// Tests that if the certificate does not contain an AIA URL, no AIA fetch
// occurs.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching, NoAIAURL) {
  leaf_->SetCaIssuersAndOCSPUrls(/*ca_issuers_urls=*/std::vector<GURL>(),
                                 /*ocsp_urls=*/std::vector<GURL>());
  TrustTestRoot();
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());
  CertVerifyResult verify_result;
  EXPECT_EQ(
      ERR_CERT_AUTHORITY_INVALID,
      proc->Verify(LeafOnly().get(), kHostname, /*ocsp_response=*/std::string(),
                   /*sct_list=*/std::string(), 0, &verify_result,
                   NetLogWithSource()));
}

// Tests that if a certificate contains one file:// URL and one http:// URL,
// there are two fetches, with the latter resulting in a successful
// verification.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching, OneFileAndOneHTTPURL) {
  const GURL kFileURL("file:///dev/null");
  leaf_->SetCaIssuersAndOCSPUrls(
      /*ca_issuers_urls=*/{kFileURL, kIntermediateURL},
      /*ocsp_urls=*/{});
  TrustTestRoot();
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  // Expect two fetches: the file:// URL (which returns an error), and the
  // http:// URL that returns a valid intermediate signed by |root_|. Though the
  // intermediate itself contains an AIA URL, it should not be fetched because
  // |root_| is in the test trust store.
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kFileURL, _, _))
      .WillOnce(Return(ByMove(
          MockCertNetFetcherRequest::Create(ERR_DISALLOWED_URL_SCHEME))));
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kIntermediateURL, _, _))
      .WillOnce(Return(ByMove(
          MockCertNetFetcherRequest::Create(intermediate_->GetCertBuffer()))));

  CertVerifyResult verify_result;
  EXPECT_EQ(OK, proc->Verify(LeafOnly().get(), kHostname,
                             /*ocsp_response=*/std::string(),
                             /*sct_list=*/std::string(), 0, &verify_result,
                             NetLogWithSource()));
}

// Tests that if an AIA request returns the wrong intermediate, certificate
// verification should fail.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching,
       UnsuccessfulVerificationWithLeafOnly) {
  TrustTestRoot();
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());
  const scoped_refptr<X509Certificate> bad_intermediate =
      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");

  EXPECT_CALL(*fetcher_, FetchCaIssuers(kIntermediateURL, _, _))
      .WillOnce(Return(ByMove(
          MockCertNetFetcherRequest::Create(bad_intermediate->cert_buffer()))));

  CertVerifyResult verify_result;
  EXPECT_EQ(
      ERR_CERT_AUTHORITY_INVALID,
      proc->Verify(LeafOnly().get(), kHostname, /*ocsp_response=*/std::string(),
                   /*sct_list=*/std::string(), 0, &verify_result,
                   NetLogWithSource()));
}

// Tests that if an AIA request returns an error, certificate verification
// should fail.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching,
       UnsuccessfulVerificationWithLeafOnlyAndErrorOnFetch) {
  TrustTestRoot();
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  EXPECT_CALL(*fetcher_, FetchCaIssuers(kIntermediateURL, _, _))
      .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(ERR_FAILED))));

  CertVerifyResult verify_result;
  EXPECT_EQ(
      ERR_CERT_AUTHORITY_INVALID,
      proc->Verify(LeafOnly().get(), kHostname, /*ocsp_response=*/std::string(),
                   /*sct_list=*/std::string(), 0, &verify_result,
                   NetLogWithSource()));
}

// Tests that if an AIA request returns an unparseable cert, certificate
// verification should fail.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching,
       UnsuccessfulVerificationWithLeafOnlyAndUnparseableFetch) {
  TrustTestRoot();
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  EXPECT_CALL(*fetcher_, FetchCaIssuers(kIntermediateURL, _, _))
      .WillOnce(Return(ByMove(CreateMockRequestWithInvalidCertificate())));

  CertVerifyResult verify_result;
  EXPECT_EQ(
      ERR_CERT_AUTHORITY_INVALID,
      proc->Verify(LeafOnly().get(), kHostname, /*ocsp_response=*/std::string(),
                   /*sct_list=*/std::string(), 0, &verify_result,
                   NetLogWithSource()));
}

// Tests that if a certificate has two HTTP AIA URLs, they are both fetched. If
// one serves an unrelated certificate and one serves a proper intermediate, the
// latter should be used to build a valid chain.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching, TwoHTTPURLs) {
  const GURL kUnrelatedURL("http://aia.test/unrelated");
  leaf_->SetCaIssuersAndOCSPUrls(
      /*ca_issuers_urls=*/{kUnrelatedURL, kIntermediateURL},
      /*ocsp_urls=*/{});
  scoped_refptr<X509Certificate> unrelated =
      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
  ASSERT_TRUE(unrelated);

  TrustTestRoot();
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  // Expect two fetches, the first of which returns an unrelated certificate
  // that is not useful in chain-building, and the second of which returns a
  // valid intermediate signed by |root_|. Though the intermediate itself
  // contains an AIA URL, it should not be fetched because |root_| is in the
  // trust store.
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kUnrelatedURL, _, _))
      .WillOnce(Return(
          ByMove(MockCertNetFetcherRequest::Create(unrelated->cert_buffer()))));
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kIntermediateURL, _, _))
      .WillOnce(Return(ByMove(
          MockCertNetFetcherRequest::Create(intermediate_->GetCertBuffer()))));

  CertVerifyResult verify_result;
  EXPECT_EQ(OK, proc->Verify(LeafOnly().get(), kHostname,
                             /*ocsp_response=*/std::string(),
                             /*sct_list=*/std::string(), 0, &verify_result,
                             NetLogWithSource()));
}

// Tests that if an intermediate is fetched via AIA, and the intermediate itself
// has an AIA URL, that URL is fetched if necessary.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching,
       AIAFetchForFetchedIntermediate) {
  // Do not set up the test root to be trusted. If the test root were trusted,
  // then the intermediate would not require an AIA fetch. With the test root
  // untrusted, the intermediate does not verify and so it will trigger an AIA
  // fetch.
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  // Expect two fetches, the first of which returns an intermediate that itself
  // has an AIA URL.
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kIntermediateURL, _, _))
      .WillOnce(Return(ByMove(
          MockCertNetFetcherRequest::Create(intermediate_->GetCertBuffer()))));
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kRootURL, _, _))
      .WillOnce(Return(
          ByMove(MockCertNetFetcherRequest::Create(root_->GetCertBuffer()))));

  CertVerifyResult verify_result;
  // This chain results in an AUTHORITY_INVALID root because |root_| is not
  // trusted.
  EXPECT_EQ(
      ERR_CERT_AUTHORITY_INVALID,
      proc->Verify(LeafOnly().get(), kHostname, /*ocsp_response=*/std::string(),
                   /*sct_list=*/std::string(), 0, &verify_result,
                   NetLogWithSource()));
}

// Tests that if a certificate contains six AIA URLs, only the first five are
// fetched, since the maximum number of fetches per Verify() call is five.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching, MaxAIAFetches) {
  leaf_->SetCaIssuersAndOCSPUrls(
      /*ca_issuers_urls=*/{GURL("http://aia.test/1"), GURL("http://aia.test/2"),
                           GURL("http://aia.test/3"), GURL("http://aia.test/4"),
                           GURL("http://aia.test/5"),
                           GURL("http://aia.test/6")},
      /*ocsp_urls=*/{});
  TrustTestRoot();
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  EXPECT_CALL(*fetcher_, FetchCaIssuers(_, _, _))
      .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(ERR_FAILED))))
      .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(ERR_FAILED))))
      .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(ERR_FAILED))))
      .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(ERR_FAILED))))
      .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(ERR_FAILED))));

  CertVerifyResult verify_result;
  EXPECT_EQ(
      ERR_CERT_AUTHORITY_INVALID,
      proc->Verify(LeafOnly().get(), kHostname, /*ocsp_response=*/std::string(),
                   /*sct_list=*/std::string(), 0, &verify_result,
                   NetLogWithSource()));
}

// Tests that if the deepest intermediate has no AIA but an earlier cert does,
// backtracking occurs and the earlier cert's AIA is used to find a cross-signed
// path to a trusted root.
//
// On pre-Q Android, the system verifier doesn't do path building on the input
// certs - it just assumes they form a single chain in order. So backtracking
// with extra certs appended won't work on those versions.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching,
       BacktrackToCrossSignedIntermediate) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_Q) {
    GTEST_SKIP() << "Skipping on pre-Q Android where system verifier does not "
                    "do path building";
  }
  // Create a separate trusted root (different from the test fixture's root_).
  auto [unused_leaf, trusted_root] = CertBuilder::CreateSimpleChain2();
  ScopedTestRoot scoped_trusted_root({trusted_root->GetX509Certificate()});

  // Create a cross-signed version of intermediate_: same public key/subject,
  // but signed by trusted_root instead of root_.
  CertBuilder cross_signed_intermediate(intermediate_->GetCertBuffer(),
                                        trusted_root.get());
  cross_signed_intermediate.SetSubjectTLV(
      base::as_byte_span(intermediate_->GetSubject()));
  cross_signed_intermediate.SetKey(bssl::UpRef(intermediate_->GetKey()));
  cross_signed_intermediate.SetSubjectKeyIdentifier(
      intermediate_->GetSubjectKeyIdentifier());

  // Remove AIA from intermediate_ so backtracking is required.
  intermediate_->SetCaIssuersAndOCSPUrls(
      /*ca_issuers_urls=*/std::vector<GURL>(),
      /*ocsp_urls=*/std::vector<GURL>());

  // leaf_ has AIA pointing to cross-signed intermediate.
  const GURL kCrossSignedURL("http://aia.test/cross-signed");
  leaf_->SetCaIssuersUrl(kCrossSignedURL);

  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  // Chain sent: leaf → intermediate → root_ (untrusted).
  // intermediate_ has no AIA, so backtrack to leaf_'s AIA.
  // Fetching returns cross_signed_intermediate (signed by trusted_root).
  // New chain: leaf → cross_signed_intermediate → trusted_root (trusted).
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kCrossSignedURL, _, _))
      .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(
          cross_signed_intermediate.GetCertBuffer()))));

  CertVerifyResult verify_result;
  EXPECT_EQ(OK, proc->Verify(LeafWithIntermediate().get(), kHostname,
                             /*ocsp_response=*/std::string(),
                             /*sct_list=*/std::string(), 0, &verify_result,
                             NetLogWithSource()));

  // Verify the returned chain has the expected certs.
  ASSERT_TRUE(verify_result.verified_cert);
  EXPECT_TRUE(x509_util::CryptoBufferEqual(
      verify_result.verified_cert->cert_buffer(), leaf_->GetCertBuffer()));
  // The verified chain may also include the trusted root, so check that the
  // first intermediate is the cross-signed one.
  ASSERT_GE(verify_result.verified_cert->intermediate_buffers().size(), 1u);
  EXPECT_TRUE(x509_util::CryptoBufferEqual(
      verify_result.verified_cert->intermediate_buffers()[0].get(),
      cross_signed_intermediate.GetCertBuffer()));
}

// Tests that if the intermediate has an AIA URL pointing to an untrusted root,
// that root is fetched and verification fails, and then backtracking occurs to
// try the leaf's AIA URL which fetches a cross-signed intermediate that leads
// to a trusted root.
//
// On pre-Q Android, the system verifier doesn't do path building on the input
// certs - it just assumes they form a single chain in order. So backtracking
// with extra certs appended won't work on those versions.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching,
       BacktrackAfterIntermediateAIAFetchesUntrustedRoot) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_Q) {
    GTEST_SKIP() << "Skipping on pre-Q Android where system verifier does not "
                    "do path building";
  }
  // Create a separate trusted root (different from the test fixture's root_).
  auto [unused_leaf, trusted_root] = CertBuilder::CreateSimpleChain2();
  ScopedTestRoot scoped_trusted_root({trusted_root->GetX509Certificate()});

  // Create a cross-signed version of intermediate_: same public key/subject,
  // but signed by trusted_root instead of root_.
  CertBuilder cross_signed_intermediate(intermediate_->GetCertBuffer(),
                                        trusted_root.get());
  cross_signed_intermediate.SetSubjectTLV(
      base::as_byte_span(intermediate_->GetSubject()));
  cross_signed_intermediate.SetKey(bssl::UpRef(intermediate_->GetKey()));
  cross_signed_intermediate.SetSubjectKeyIdentifier(
      intermediate_->GetSubjectKeyIdentifier());

  // leaf_ has AIA pointing to cross-signed intermediate.
  const GURL kCrossSignedURL("http://aia.test/cross-signed");
  leaf_->SetCaIssuersUrl(kCrossSignedURL);

  // intermediate_ keeps its AIA pointing to kRootURL (set in SetUp).

  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  // Chain sent: leaf → intermediate → root_ (untrusted).
  // First, intermediate_'s AIA (kRootURL) fetches root_, but root_ is
  // untrusted and self-signed, so verification fails.
  // Then backtrack to leaf_'s AIA (kCrossSignedURL), which fetches
  // cross_signed_intermediate (signed by trusted_root).
  // New chain: leaf → cross_signed_intermediate → trusted_root (trusted).
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kRootURL, _, _))
      .WillOnce(Return(
          ByMove(MockCertNetFetcherRequest::Create(root_->GetCertBuffer()))));
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kCrossSignedURL, _, _))
      .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(
          cross_signed_intermediate.GetCertBuffer()))));

  CertVerifyResult verify_result;
  EXPECT_EQ(OK, proc->Verify(LeafWithIntermediate().get(), kHostname,
                             /*ocsp_response=*/std::string(),
                             /*sct_list=*/std::string(), 0, &verify_result,
                             NetLogWithSource()));

  // Verify the returned chain has the expected certs.
  ASSERT_TRUE(verify_result.verified_cert);
  EXPECT_TRUE(x509_util::CryptoBufferEqual(
      verify_result.verified_cert->cert_buffer(), leaf_->GetCertBuffer()));
  ASSERT_GE(verify_result.verified_cert->intermediate_buffers().size(), 1u);
  EXPECT_TRUE(x509_util::CryptoBufferEqual(
      verify_result.verified_cert->intermediate_buffers()[0].get(),
      cross_signed_intermediate.GetCertBuffer()));
}

// Tests that cross-signed certificates work (without AIA fetching).
// This verifies the test infrastructure for cross-signing is correct.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching, CrossSignedCertWithoutAIA) {
  // Create a separate trusted root (different from the test fixture's root_).
  auto [unused_leaf, trusted_root] = CertBuilder::CreateSimpleChain2();
  ScopedTestRoot scoped_trusted_root({trusted_root->GetX509Certificate()});

  // Create a cross-signed version of intermediate_: same public key/subject,
  // but signed by trusted_root instead of root_.
  // Use intermediate_ as template, then override subject/key/SKI to ensure
  // they match exactly (since CertBuilder randomizes these by default).
  CertBuilder cross_signed_intermediate(intermediate_->GetCertBuffer(),
                                        trusted_root.get());
  cross_signed_intermediate.SetSubjectTLV(
      base::as_byte_span(intermediate_->GetSubject()));
  cross_signed_intermediate.SetKey(bssl::UpRef(intermediate_->GetKey()));
  cross_signed_intermediate.SetSubjectKeyIdentifier(
      intermediate_->GetSubjectKeyIdentifier());

  // Build a chain with leaf and the cross-signed intermediate.
  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
  intermediates.push_back(
      bssl::UpRef(cross_signed_intermediate.GetCertBuffer()));
  scoped_refptr<X509Certificate> chain = X509Certificate::CreateFromBuffer(
      bssl::UpRef(leaf_->GetCertBuffer()), std::move(intermediates));
  ASSERT_TRUE(chain);

  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  // No AIA fetches expected - chain is complete.
  CertVerifyResult verify_result;
  EXPECT_EQ(OK, proc->Verify(chain.get(), kHostname,
                             /*ocsp_response=*/std::string(),
                             /*sct_list=*/std::string(), 0, &verify_result,
                             NetLogWithSource()));
}

// Tests that if an AIA fetch returns a certificate already present in the
// chain (a duplicate), it is detected and skipped without triggering
// re-verification. This exercises the duplicate-detection loop that handles
// the case where an AIA response may contain multiple certificates.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching, DuplicateFetchedCertSkipped) {
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  // Leaf's AIA (kIntermediateURL) fetches the intermediate, which is new and
  // gets added. Re-verification fails because root_ is not trusted.
  // Then intermediate's AIA (kRootURL) fetches the intermediate again — this
  // is a duplicate and should be skipped without triggering re-verification.
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kIntermediateURL, _, _))
      .WillOnce(Return(ByMove(
          MockCertNetFetcherRequest::Create(intermediate_->GetCertBuffer()))));
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kRootURL, _, _))
      .WillOnce(Return(ByMove(
          MockCertNetFetcherRequest::Create(intermediate_->GetCertBuffer()))));

  CertVerifyResult verify_result;
  EXPECT_EQ(
      ERR_CERT_AUTHORITY_INVALID,
      proc->Verify(LeafOnly().get(), kHostname, /*ocsp_response=*/std::string(),
                   /*sct_list=*/std::string(), 0, &verify_result,
                   NetLogWithSource()));
}

// Tests that if the supplied chain contains an intermediate with an AIA URL,
// that AIA URL is fetched if necessary.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching, FetchForSuppliedIntermediate) {
  // Do not set up the test root to be trusted. If the test root were trusted,
  // then the intermediate would not require an AIA fetch. With the test root
  // untrusted, the intermediate does not verify and so it will trigger an AIA
  // fetch.
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>(fetcher_,
                                                  CRLSet::BuiltinCRLSet());

  EXPECT_CALL(*fetcher_, FetchCaIssuers(kRootURL, _, _))
      .WillOnce(Return(
          ByMove(MockCertNetFetcherRequest::Create(root_->GetCertBuffer()))));

  // After fetching root_ (which is untrusted), backtracking will also try
  // leaf_'s AIA URL. Return an error since it won't help either.
  EXPECT_CALL(*fetcher_, FetchCaIssuers(kIntermediateURL, _, _))
      .WillOnce(Return(ByMove(MockCertNetFetcherRequest::Create(ERR_FAILED))));

  CertVerifyResult verify_result;
  // This chain results in an AUTHORITY_INVALID root because |root_| is not
  // trusted.
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
            proc->Verify(LeafWithIntermediate().get(), kHostname,
                         /*ocsp_response=*/std::string(),
                         /*sct_list=*/std::string(), 0, &verify_result,
                         NetLogWithSource()));
}

}  // namespace net
