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

#include "net/cert/cert_verify_proc_android.h"

#include <memory>
#include <vector>

#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/internal/test_helpers.h"
#include "net/cert/test_root_certs.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.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::ByMove;
using ::testing::Return;
using ::testing::_;

namespace net {

namespace {

// A CertNetFetcher::Request whose WaitForResult() method always
// immediately returns the |error| and |bytes| provided in its
// constructor.
class TestCertNetFetcherRequest : public CertNetFetcher::Request {
 public:
  TestCertNetFetcherRequest(Error error, const std::vector<uint8_t>& bytes)
      : error_(error), bytes_(bytes) {}
  ~TestCertNetFetcherRequest() override {}

  void WaitForResult(Error* error, std::vector<uint8_t>* bytes) override {
    *error = error_;
    *bytes = bytes_;
  }

 private:
  Error error_;
  std::vector<uint8_t> bytes_;
};

class MockCertNetFetcher : public CertNetFetcher {
 public:
  MockCertNetFetcher() {}

  MOCK_METHOD0(Shutdown, void());
  MOCK_METHOD3(FetchCaIssuers, std::unique_ptr<Request>(const GURL&, int, int));
  MOCK_METHOD3(FetchCrl, std::unique_ptr<Request>(const GURL&, int, int));
  MOCK_METHOD3(FetchOcsp, std::unique_ptr<Request>(const GURL&, int, int));

 private:
  ~MockCertNetFetcher() override {}
};

std::unique_ptr<CertNetFetcher::Request> CreateMockRequestFromX509Certificate(
    Error error,
    const scoped_refptr<X509Certificate>& cert) {
  base::StringPiece der =
      x509_util::CryptoBufferAsStringPiece(cert->cert_buffer());
  return std::make_unique<TestCertNetFetcherRequest>(
      error, std::vector<uint8_t>(der.data(), der.data() + der.length()));
}

std::unique_ptr<CertNetFetcher::Request> CreateMockRequestWithError(
    Error error) {
  return std::make_unique<TestCertNetFetcherRequest>(error,
                                                     std::vector<uint8_t>({}));
}

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

::testing::AssertionResult ReadTestPem(const std::string& file_name,
                                       const std::string& block_name,
                                       std::string* result) {
  const PemBlockMapping mappings[] = {
      {block_name.c_str(), result},
  };

  return ReadTestDataFromPemFile(file_name, mappings);
}

::testing::AssertionResult ReadTestCert(
    const std::string& file_name,
    scoped_refptr<X509Certificate>* result) {
  std::string der;
  ::testing::AssertionResult r =
      ReadTestPem("net/data/cert_issuer_source_aia_unittest/" + file_name,
                  "CERTIFICATE", &der);
  if (!r)
    return r;
  *result = X509Certificate::CreateFromBytes(der.data(), der.length());
  if (!result) {
    return ::testing::AssertionFailure()
           << "X509Certificate::CreateFromBytes() failed";
  }
  return ::testing::AssertionSuccess();
}

::testing::AssertionResult ReadTestAIARoot(
    scoped_refptr<X509Certificate>* result) {
  return ReadTestCert("root.pem", result);
}

::testing::AssertionResult CreateCertificateChainFromFiles(
    const std::vector<std::string>& files,
    scoped_refptr<X509Certificate>* result) {
  scoped_refptr<X509Certificate> leaf;
  ::testing::AssertionResult r = ReadTestCert(files[0], &leaf);
  if (!r)
    return r;
  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediate_buffers;
  for (size_t i = 1; i < files.size(); i++) {
    scoped_refptr<X509Certificate> intermediate;
    r = ReadTestCert(files[i], &intermediate);
    if (!r)
      return r;
    intermediate_buffers.push_back(bssl::UpRef(intermediate->cert_buffer()));
  }
  *result = X509Certificate::CreateFromBuffer(bssl::UpRef(leaf->cert_buffer()),
                                              std::move(intermediate_buffers));
  return ::testing::AssertionSuccess();
}

// 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>();
    SetGlobalCertNetFetcherForTesting(fetcher_);
  }

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

 protected:
  ::testing::AssertionResult SetUpTestRoot() {
    ::testing::AssertionResult r = ReadTestAIARoot(&root_);
    if (!r)
      return r;
    scoped_test_root_.reset(new ScopedTestRoot(root_.get()));
    return ::testing::AssertionSuccess();
  }

  scoped_refptr<MockCertNetFetcher> fetcher_;
  const CertificateList empty_cert_list_;

 private:
  scoped_refptr<X509Certificate> root_;
  std::unique_ptr<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) {
  ASSERT_TRUE(SetUpTestRoot());
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>();
  scoped_refptr<X509Certificate> leaf;
  ASSERT_TRUE(
      CreateCertificateChainFromFiles({"target_one_aia.pem", "i.pem"}, &leaf));
  CertVerifyResult verify_result;
  EXPECT_EQ(OK, proc->Verify(leaf.get(), "target", std::string(), 0, nullptr,
                             empty_cert_list_, &verify_result));
}

// Tests that if the certificate does not contain an AIA URL, no AIA fetch
// occurs.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching, NoAIAURL) {
  ASSERT_TRUE(SetUpTestRoot());
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>();
  scoped_refptr<X509Certificate> cert;
  ASSERT_TRUE(ReadTestCert("target_no_aia.pem", &cert));
  CertVerifyResult verify_result;
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
                         empty_cert_list_, &verify_result));
}

// 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) {
  ASSERT_TRUE(SetUpTestRoot());
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>();
  scoped_refptr<X509Certificate> cert;
  ASSERT_TRUE(ReadTestCert("target_file_and_http_aia.pem", &cert));
  scoped_refptr<X509Certificate> intermediate;
  ASSERT_TRUE(ReadTestCert("i2.pem", &intermediate));

  // 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(GURL("file:///dev/null"), _, _))
      .WillOnce(Return(
          ByMove(CreateMockRequestWithError(ERR_DISALLOWED_URL_SCHEME))));
  EXPECT_CALL(*fetcher_,
              FetchCaIssuers(GURL("http://url-for-aia2/I2.foo"), _, _))
      .WillOnce(Return(
          ByMove(CreateMockRequestFromX509Certificate(OK, intermediate))));

  CertVerifyResult verify_result;
  EXPECT_EQ(OK, proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
                             empty_cert_list_, &verify_result));
}

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

  EXPECT_CALL(*fetcher_, FetchCaIssuers(GURL("http://url-for-aia/I.cer"), _, _))
      .WillOnce(Return(
          ByMove(CreateMockRequestFromX509Certificate(OK, bad_intermediate))));

  CertVerifyResult verify_result;
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
                         empty_cert_list_, &verify_result));
}

// Tests that if an AIA request returns an error, certificate verification
// should fail.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching,
       UnsuccessfulVerificationWithLeafOnlyAndErrorOnFetch) {
  ASSERT_TRUE(SetUpTestRoot());
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>();
  scoped_refptr<X509Certificate> cert;
  ASSERT_TRUE(ReadTestCert("target_one_aia.pem", &cert));

  EXPECT_CALL(*fetcher_, FetchCaIssuers(GURL("http://url-for-aia/I.cer"), _, _))
      .WillOnce(Return(ByMove(CreateMockRequestWithError(ERR_FAILED))));

  CertVerifyResult verify_result;
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
                         empty_cert_list_, &verify_result));
}

// Tests that if an AIA request returns an unparseable cert, certificate
// verification should fail.
TEST_F(CertVerifyProcAndroidTestWithAIAFetching,
       UnsuccessfulVerificationWithLeafOnlyAndUnparseableFetch) {
  ASSERT_TRUE(SetUpTestRoot());
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>();
  scoped_refptr<X509Certificate> cert;
  ASSERT_TRUE(ReadTestCert("target_one_aia.pem", &cert));

  EXPECT_CALL(*fetcher_, FetchCaIssuers(GURL("http://url-for-aia/I.cer"), _, _))
      .WillOnce(Return(ByMove(CreateMockRequestWithInvalidCertificate())));

  CertVerifyResult verify_result;
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
                         empty_cert_list_, &verify_result));
}

// 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) {
  ASSERT_TRUE(SetUpTestRoot());
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>();
  scoped_refptr<X509Certificate> cert;
  ASSERT_TRUE(ReadTestCert("target_two_aia.pem", &cert));
  scoped_refptr<X509Certificate> intermediate;
  ASSERT_TRUE(ReadTestCert("i2.pem", &intermediate));
  scoped_refptr<X509Certificate> unrelated;
  ASSERT_TRUE(ReadTestCert("target_three_aia.pem", &unrelated));

  // 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(GURL("http://url-for-aia/I.cer"), _, _))
      .WillOnce(
          Return(ByMove(CreateMockRequestFromX509Certificate(OK, unrelated))));
  EXPECT_CALL(*fetcher_,
              FetchCaIssuers(GURL("http://url-for-aia2/I2.foo"), _, _))
      .WillOnce(Return(
          ByMove(CreateMockRequestFromX509Certificate(OK, intermediate))));

  CertVerifyResult verify_result;
  EXPECT_EQ(OK, proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
                             empty_cert_list_, &verify_result));
}

// 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 i2.pem would not require an AIA fetch. With the test
  // root untrusted, i2.pem does not verify and so it will trigger an AIA fetch.
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>();
  scoped_refptr<X509Certificate> cert;
  ASSERT_TRUE(ReadTestCert("target_one_aia.pem", &cert));
  scoped_refptr<X509Certificate> intermediate;
  ASSERT_TRUE(ReadTestCert("i2.pem", &intermediate));
  scoped_refptr<X509Certificate> root;
  ASSERT_TRUE(ReadTestAIARoot(&root));

  // Expect two fetches, the first of which returns an intermediate that itself
  // has an AIA URL.
  EXPECT_CALL(*fetcher_, FetchCaIssuers(GURL("http://url-for-aia/I.cer"), _, _))
      .WillOnce(Return(
          ByMove(CreateMockRequestFromX509Certificate(OK, intermediate))));
  EXPECT_CALL(*fetcher_,
              FetchCaIssuers(GURL("http://url-for-aia/Root.cer"), _, _))
      .WillOnce(Return(ByMove(CreateMockRequestFromX509Certificate(OK, root))));

  CertVerifyResult verify_result;
  // This chain results in an AUTHORITY_INVALID root because |root_| is not
  // trusted.
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
                         empty_cert_list_, &verify_result));
}

// 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) {
  ASSERT_TRUE(SetUpTestRoot());
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>();
  scoped_refptr<X509Certificate> cert;
  ASSERT_TRUE(ReadTestCert("target_six_aia.pem", &cert));

  EXPECT_CALL(*fetcher_, FetchCaIssuers(_, _, _))
      .WillOnce(Return(ByMove(CreateMockRequestWithError(ERR_FAILED))))
      .WillOnce(Return(ByMove(CreateMockRequestWithError(ERR_FAILED))))
      .WillOnce(Return(ByMove(CreateMockRequestWithError(ERR_FAILED))))
      .WillOnce(Return(ByMove(CreateMockRequestWithError(ERR_FAILED))))
      .WillOnce(Return(ByMove(CreateMockRequestWithError(ERR_FAILED))));

  CertVerifyResult verify_result;
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
                         empty_cert_list_, &verify_result));
}

// 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 i.pem would not require an AIA fetch. With the test
  // root untrusted, i.pem does not verify and so it will trigger an AIA fetch.
  scoped_refptr<CertVerifyProcAndroid> proc =
      base::MakeRefCounted<CertVerifyProcAndroid>();
  scoped_refptr<X509Certificate> leaf;
  ASSERT_TRUE(
      CreateCertificateChainFromFiles({"target_one_aia.pem", "i.pem"}, &leaf));
  scoped_refptr<X509Certificate> root;
  ASSERT_TRUE(ReadTestAIARoot(&root));

  EXPECT_CALL(*fetcher_,
              FetchCaIssuers(GURL("http://url-for-aia/Root.cer"), _, _))
      .WillOnce(Return(ByMove(CreateMockRequestFromX509Certificate(OK, root))));

  CertVerifyResult verify_result;
  // This chain results in an AUTHORITY_INVALID root because |root_| is not
  // trusted.
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
            proc->Verify(leaf.get(), "target", std::string(), 0, nullptr,
                         empty_cert_list_, &verify_result));
}

}  // namespace net
