// Copyright (c) 2012 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 <cert.h>
#include <certdb.h>
#include <pk11pub.h>

#include <algorithm>

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/string16.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "crypto/nss_util.h"
#include "crypto/nss_util_internal.h"
#include "crypto/scoped_nss_types.h"
#include "net/base/cert_status_flags.h"
#include "net/base/cert_test_util.h"
#include "net/base/cert_verify_proc_nss.h"
#include "net/base/cert_verify_result.h"
#include "net/base/crypto_module.h"
#include "net/base/net_errors.h"
#include "net/base/nss_cert_database.h"
#include "net/base/x509_certificate.h"
#include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
#include "testing/gtest/include/gtest/gtest.h"

// In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
// the new name of the macro.
#if !defined(CERTDB_TERMINAL_RECORD)
#define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
#endif

namespace net {

// TODO(mattm): when https://bugzilla.mozilla.org/show_bug.cgi?id=588269 is
// fixed, switch back to using a separate userdb for each test.
// (When doing so, remember to add some standalone tests of DeleteCert since it
// won't be tested by TearDown anymore.)
class CertDatabaseNSSTest : public testing::Test {
 public:
  static void SetUpTestCase() {
    ASSERT_TRUE(crypto::OpenTestNSSDB());
    // There is no matching TearDownTestCase call to close the test NSS DB
    // because that would leave NSS in a potentially broken state for further
    // tests, due to https://bugzilla.mozilla.org/show_bug.cgi?id=588269
  }

  virtual void SetUp() {
    cert_db_ = NSSCertDatabase::GetInstance();
    slot_ = cert_db_->GetPublicModule();

    // Test db should be empty at start of test.
    EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
  }

  virtual void TearDown() {
    // Don't try to cleanup if the setup failed.
    ASSERT_TRUE(slot_->os_module_handle());

    EXPECT_TRUE(CleanupSlotContents());

    // Run the message loop to process any observer callbacks (e.g. for the
    // ClientSocketFactory singleton) so that the scoped ref ptrs created in
    // NSSCertDatabase::NotifyObservers* get released.
    MessageLoop::current()->RunAllPending();

    EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
  }

 protected:
  static std::string ReadTestFile(const std::string& name) {
    std::string result;
    FilePath cert_path = GetTestCertsDirectory().AppendASCII(name);
    EXPECT_TRUE(file_util::ReadFileToString(cert_path, &result));
    return result;
  }

  static bool ReadCertIntoList(const std::string& name,
                               CertificateList* certs) {
    scoped_refptr<X509Certificate> cert(
        ImportCertFromFile(GetTestCertsDirectory(), name));
    if (!cert.get())
      return false;

    certs->push_back(cert);
    return true;
  }

  static CertificateList ListCertsInSlot(PK11SlotInfo* slot) {
    CertificateList result;
    CERTCertList* cert_list = PK11_ListCertsInSlot(slot);
    for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
         !CERT_LIST_END(node, cert_list);
         node = CERT_LIST_NEXT(node)) {
      result.push_back(X509Certificate::CreateFromHandle(
          node->cert, X509Certificate::OSCertHandles()));
    }
    CERT_DestroyCertList(cert_list);

    // Sort the result so that test comparisons can be deterministic.
    std::sort(result.begin(), result.end(), X509Certificate::LessThan());
    return result;
  }

  scoped_refptr<CryptoModule> slot_;
  NSSCertDatabase* cert_db_;

 private:
  bool CleanupSlotContents() {
    bool ok = true;
    CertificateList certs = ListCertsInSlot(slot_->os_module_handle());
    CERTCertTrust default_trust = {0};
    for (size_t i = 0; i < certs.size(); ++i) {
      // Reset cert trust values to defaults before deleting.  Otherwise NSS
      // somehow seems to remember the trust which can break following tests.
      SECStatus srv = CERT_ChangeCertTrust(
          CERT_GetDefaultCertDB(), certs[i]->os_cert_handle(), &default_trust);
      if (srv != SECSuccess)
        ok = false;

      if (!cert_db_->DeleteCertAndKey(certs[i]))
        ok = false;
    }
    return ok;
  }
};

TEST_F(CertDatabaseNSSTest, ListCerts) {
  // This test isn't terribly useful, though it will at least let valgrind test
  // for leaks.
  CertificateList certs;
  cert_db_->ListCerts(&certs);
  // The test DB is empty, but let's assume there will always be something in
  // the other slots.
  EXPECT_LT(0U, certs.size());
}

TEST_F(CertDatabaseNSSTest, ImportFromPKCS12WrongPassword) {
  std::string pkcs12_data = ReadTestFile("client.p12");

  EXPECT_EQ(ERR_PKCS12_IMPORT_BAD_PASSWORD,
            cert_db_->ImportFromPKCS12(slot_,
                                       pkcs12_data,
                                       string16(),
                                       true,  // is_extractable
                                       NULL));

  // Test db should still be empty.
  EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
}

TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AsExtractableAndExportAgain) {
  std::string pkcs12_data = ReadTestFile("client.p12");

  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(slot_,
                                           pkcs12_data,
                                           ASCIIToUTF16("12345"),
                                           true,  // is_extractable
                                           NULL));

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  scoped_refptr<X509Certificate> cert(cert_list[0]);

  EXPECT_EQ("testusercert",
            cert->subject().common_name);

  // TODO(mattm): move export test to separate test case?
  std::string exported_data;
  EXPECT_EQ(1, cert_db_->ExportToPKCS12(cert_list, ASCIIToUTF16("exportpw"),
                                        &exported_data));
  ASSERT_LT(0U, exported_data.size());
  // TODO(mattm): further verification of exported data?
}

TEST_F(CertDatabaseNSSTest, ImportFromPKCS12Twice) {
  std::string pkcs12_data = ReadTestFile("client.p12");

  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(slot_,
                                           pkcs12_data,
                                           ASCIIToUTF16("12345"),
                                           true,  // is_extractable
                                           NULL));
  EXPECT_EQ(1U, ListCertsInSlot(slot_->os_module_handle()).size());

  // NSS has a SEC_ERROR_PKCS12_DUPLICATE_DATA error, but it doesn't look like
  // it's ever used.  This test verifies that.
  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(slot_,
                                           pkcs12_data,
                                           ASCIIToUTF16("12345"),
                                           true,  // is_extractable
                                           NULL));
  EXPECT_EQ(1U, ListCertsInSlot(slot_->os_module_handle()).size());
}

TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AsUnextractableAndExportAgain) {
  std::string pkcs12_data = ReadTestFile("client.p12");

  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(slot_,
                                           pkcs12_data,
                                           ASCIIToUTF16("12345"),
                                           false,  // is_extractable
                                           NULL));

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  scoped_refptr<X509Certificate> cert(cert_list[0]);

  EXPECT_EQ("testusercert",
            cert->subject().common_name);

  std::string exported_data;
  EXPECT_EQ(0, cert_db_->ExportToPKCS12(cert_list, ASCIIToUTF16("exportpw"),
                                        &exported_data));
}

// Importing a PKCS#12 file with a certificate but no corresponding
// private key should not mark an existing private key as unextractable.
TEST_F(CertDatabaseNSSTest, ImportFromPKCS12OnlyMarkIncludedKey) {
  std::string pkcs12_data = ReadTestFile("client.p12");
  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(slot_,
                                           pkcs12_data,
                                           ASCIIToUTF16("12345"),
                                           true,  // is_extractable
                                           NULL));

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());

  // Now import a PKCS#12 file with just a certificate but no private key.
  pkcs12_data = ReadTestFile("client-nokey.p12");
  EXPECT_EQ(OK, cert_db_->ImportFromPKCS12(slot_,
                                           pkcs12_data,
                                           ASCIIToUTF16("12345"),
                                           false,  // is_extractable
                                           NULL));

  cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());

  // Make sure the imported private key is still extractable.
  std::string exported_data;
  EXPECT_EQ(1, cert_db_->ExportToPKCS12(cert_list, ASCIIToUTF16("exportpw"),
                                        &exported_data));
  ASSERT_LT(0U, exported_data.size());
}

TEST_F(CertDatabaseNSSTest, ImportFromPKCS12InvalidFile) {
  std::string pkcs12_data = "Foobarbaz";

  EXPECT_EQ(ERR_PKCS12_IMPORT_INVALID_FILE,
            cert_db_->ImportFromPKCS12(slot_,
                                       pkcs12_data,
                                       string16(),
                                       true,  // is_extractable
                                       NULL));

  // Test db should still be empty.
  EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
}

TEST_F(CertDatabaseNSSTest, ImportCACert_SSLTrust) {
  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "root_ca_cert.crt",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());
  EXPECT_FALSE(certs[0]->os_cert_handle()->isperm);

  // Import it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUSTED_SSL,
                                      &failed));

  EXPECT_EQ(0U, failed.size());

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  scoped_refptr<X509Certificate> cert(cert_list[0]);
  EXPECT_EQ("Test CA", cert->subject().common_name);

  EXPECT_EQ(NSSCertDatabase::TRUSTED_SSL,
            cert_db_->GetCertTrust(cert.get(), CA_CERT));

  EXPECT_EQ(unsigned(CERTDB_VALID_CA | CERTDB_TRUSTED_CA |
                     CERTDB_TRUSTED_CLIENT_CA),
            cert->os_cert_handle()->trust->sslFlags);
  EXPECT_EQ(unsigned(CERTDB_VALID_CA),
            cert->os_cert_handle()->trust->emailFlags);
  EXPECT_EQ(unsigned(CERTDB_VALID_CA),
            cert->os_cert_handle()->trust->objectSigningFlags);
}

TEST_F(CertDatabaseNSSTest, ImportCACert_EmailTrust) {
  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "root_ca_cert.crt",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());
  EXPECT_FALSE(certs[0]->os_cert_handle()->isperm);

  // Import it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUSTED_EMAIL,
                                      &failed));

  EXPECT_EQ(0U, failed.size());

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  scoped_refptr<X509Certificate> cert(cert_list[0]);
  EXPECT_EQ("Test CA", cert->subject().common_name);

  EXPECT_EQ(NSSCertDatabase::TRUSTED_EMAIL,
            cert_db_->GetCertTrust(cert.get(), CA_CERT));

  EXPECT_EQ(unsigned(CERTDB_VALID_CA),
            cert->os_cert_handle()->trust->sslFlags);
  EXPECT_EQ(unsigned(CERTDB_VALID_CA | CERTDB_TRUSTED_CA |
                     CERTDB_TRUSTED_CLIENT_CA),
            cert->os_cert_handle()->trust->emailFlags);
  EXPECT_EQ(unsigned(CERTDB_VALID_CA),
            cert->os_cert_handle()->trust->objectSigningFlags);
}

TEST_F(CertDatabaseNSSTest, ImportCACert_ObjSignTrust) {
  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "root_ca_cert.crt",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());
  EXPECT_FALSE(certs[0]->os_cert_handle()->isperm);

  // Import it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUSTED_OBJ_SIGN,
                                      &failed));

  EXPECT_EQ(0U, failed.size());

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  scoped_refptr<X509Certificate> cert(cert_list[0]);
  EXPECT_EQ("Test CA", cert->subject().common_name);

  EXPECT_EQ(NSSCertDatabase::TRUSTED_OBJ_SIGN,
            cert_db_->GetCertTrust(cert.get(), CA_CERT));

  EXPECT_EQ(unsigned(CERTDB_VALID_CA),
            cert->os_cert_handle()->trust->sslFlags);
  EXPECT_EQ(unsigned(CERTDB_VALID_CA),
            cert->os_cert_handle()->trust->emailFlags);
  EXPECT_EQ(unsigned(CERTDB_VALID_CA | CERTDB_TRUSTED_CA |
                     CERTDB_TRUSTED_CLIENT_CA),
            cert->os_cert_handle()->trust->objectSigningFlags);
}

TEST_F(CertDatabaseNSSTest, ImportCA_NotCACert) {
  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "google.single.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());
  EXPECT_FALSE(certs[0]->os_cert_handle()->isperm);

  // Import it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUSTED_SSL,
                                      &failed));
  ASSERT_EQ(1U, failed.size());
  // Note: this compares pointers directly.  It's okay in this case because
  // ImportCACerts returns the same pointers that were passed in.  In the
  // general case IsSameOSCert should be used.
  EXPECT_EQ(certs[0], failed[0].certificate);
  EXPECT_EQ(ERR_IMPORT_CA_CERT_NOT_CA, failed[0].net_error);

  EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
}

TEST_F(CertDatabaseNSSTest, ImportCACertHierarchy) {
  CertificateList certs;
  ASSERT_TRUE(ReadCertIntoList("dod_root_ca_2_cert.der", &certs));
  ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));
  ASSERT_TRUE(ReadCertIntoList("www_us_army_mil_cert.der", &certs));

  // Import it.
  NSSCertDatabase::ImportCertFailureList failed;
  // Have to specify email trust for the cert verification of the child cert to
  // work (see
  // http://mxr.mozilla.org/mozilla/source/security/nss/lib/certhigh/certvfy.c#752
  // "XXX This choice of trustType seems arbitrary.")
  EXPECT_TRUE(cert_db_->ImportCACerts(
      certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL,
      &failed));

  ASSERT_EQ(2U, failed.size());
  EXPECT_EQ("DOD CA-17", failed[0].certificate->subject().common_name);
  EXPECT_EQ(ERR_FAILED, failed[0].net_error);  // The certificate expired.
  EXPECT_EQ("www.us.army.mil", failed[1].certificate->subject().common_name);
  EXPECT_EQ(ERR_IMPORT_CA_CERT_NOT_CA, failed[1].net_error);

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);
}

TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyDupeRoot) {
  CertificateList certs;
  ASSERT_TRUE(ReadCertIntoList("dod_root_ca_2_cert.der", &certs));

  // First import just the root.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(
      certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL,
      &failed));

  EXPECT_EQ(0U, failed.size());
  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);

  ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));
  ASSERT_TRUE(ReadCertIntoList("www_us_army_mil_cert.der", &certs));

  // Now import with the other certs in the list too.  Even though the root is
  // already present, we should still import the rest.
  failed.clear();
  EXPECT_TRUE(cert_db_->ImportCACerts(
      certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL,
      &failed));

  ASSERT_EQ(3U, failed.size());
  EXPECT_EQ("DoD Root CA 2", failed[0].certificate->subject().common_name);
  EXPECT_EQ(ERR_IMPORT_CERT_ALREADY_EXISTS, failed[0].net_error);
  EXPECT_EQ("DOD CA-17", failed[1].certificate->subject().common_name);
  EXPECT_EQ(ERR_FAILED, failed[1].net_error);  // The certificate expired.
  EXPECT_EQ("www.us.army.mil", failed[2].certificate->subject().common_name);
  EXPECT_EQ(ERR_IMPORT_CA_CERT_NOT_CA, failed[2].net_error);

  cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);
}

TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyUntrusted) {
  CertificateList certs;
  ASSERT_TRUE(ReadCertIntoList("dod_root_ca_2_cert.der", &certs));
  ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));

  // Import it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUST_DEFAULT,
                                      &failed));

  ASSERT_EQ(1U, failed.size());
  EXPECT_EQ("DOD CA-17", failed[0].certificate->subject().common_name);
  // TODO(mattm): should check for net error equivalent of
  // SEC_ERROR_UNTRUSTED_ISSUER
  EXPECT_EQ(ERR_FAILED, failed[0].net_error);

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);
}

TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyTree) {
  CertificateList certs;
  ASSERT_TRUE(ReadCertIntoList("dod_root_ca_2_cert.der", &certs));
  ASSERT_TRUE(ReadCertIntoList("dod_ca_13_cert.der", &certs));
  ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));

  // Import it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(
      certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL,
      &failed));

  EXPECT_EQ(2U, failed.size());
  EXPECT_EQ("DOD CA-13", failed[0].certificate->subject().common_name);
  EXPECT_EQ(ERR_FAILED, failed[0].net_error);  // The certificate expired.
  EXPECT_EQ("DOD CA-17", failed[1].certificate->subject().common_name);
  EXPECT_EQ(ERR_FAILED, failed[1].net_error);  // The certificate expired.

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);
}

TEST_F(CertDatabaseNSSTest, ImportCACertNotHierarchy) {
  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "root_ca_cert.crt",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());
  ASSERT_TRUE(ReadCertIntoList("dod_ca_13_cert.der", &certs));
  ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));

  // Import it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(
      certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL |
      NSSCertDatabase::TRUSTED_OBJ_SIGN, &failed));

  ASSERT_EQ(2U, failed.size());
  // TODO(mattm): should check for net error equivalent of
  // SEC_ERROR_UNKNOWN_ISSUER
  EXPECT_EQ("DOD CA-13", failed[0].certificate->subject().common_name);
  EXPECT_EQ(ERR_FAILED, failed[0].net_error);
  EXPECT_EQ("DOD CA-17", failed[1].certificate->subject().common_name);
  EXPECT_EQ(ERR_FAILED, failed[1].net_error);

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  EXPECT_EQ("Test CA", cert_list[0]->subject().common_name);
}

// http://crbug.com/108009 - Disabled, as google.chain.pem is an expired
// certificate.
TEST_F(CertDatabaseNSSTest, DISABLED_ImportServerCert) {
  // Need to import intermediate cert for the verify of google cert, otherwise
  // it will try to fetch it automatically with cert_pi_useAIACertFetch, which
  // will cause OCSPCreateSession on the main thread, which is not allowed.
  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "google.chain.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(2U, certs.size());

  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportServerCert(certs, NSSCertDatabase::TRUST_DEFAULT,
                                         &failed));

  EXPECT_EQ(0U, failed.size());

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(2U, cert_list.size());
  scoped_refptr<X509Certificate> goog_cert(cert_list[0]);
  scoped_refptr<X509Certificate> thawte_cert(cert_list[1]);
  EXPECT_EQ("www.google.com", goog_cert->subject().common_name);
  EXPECT_EQ("Thawte SGC CA", thawte_cert->subject().common_name);

  EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
            cert_db_->GetCertTrust(goog_cert.get(), SERVER_CERT));

  EXPECT_EQ(0U, goog_cert->os_cert_handle()->trust->sslFlags);

  scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
  int flags = 0;
  CertVerifyResult verify_result;
  int error = verify_proc->Verify(goog_cert, "www.google.com", flags,
                                  NULL, &verify_result);
  EXPECT_EQ(OK, error);
  EXPECT_EQ(0U, verify_result.cert_status);
}

TEST_F(CertDatabaseNSSTest, ImportServerCert_SelfSigned) {
  CertificateList certs;
  ASSERT_TRUE(ReadCertIntoList("punycodetest.der", &certs));

  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportServerCert(certs, NSSCertDatabase::TRUST_DEFAULT,
                                         &failed));

  EXPECT_EQ(0U, failed.size());

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  scoped_refptr<X509Certificate> puny_cert(cert_list[0]);

  EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
            cert_db_->GetCertTrust(puny_cert.get(), SERVER_CERT));
  EXPECT_EQ(0U, puny_cert->os_cert_handle()->trust->sslFlags);

  scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
  int flags = 0;
  CertVerifyResult verify_result;
  int error = verify_proc->Verify(puny_cert, "xn--wgv71a119e.com", flags,
                                  NULL, &verify_result);
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
  EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
}

TEST_F(CertDatabaseNSSTest, ImportServerCert_SelfSigned_Trusted) {
  // When using CERT_PKIXVerifyCert (which we do), server trust only works from
  // 3.13.4 onwards.  See https://bugzilla.mozilla.org/show_bug.cgi?id=647364.
  if (!NSS_VersionCheck("3.13.4")) {
    LOG(INFO) << "test skipped on NSS < 3.13.4";
    return;
  }

  CertificateList certs;
  ASSERT_TRUE(ReadCertIntoList("punycodetest.der", &certs));

  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportServerCert(certs, NSSCertDatabase::TRUSTED_SSL,
                                         &failed));

  EXPECT_EQ(0U, failed.size());

  CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
  ASSERT_EQ(1U, cert_list.size());
  scoped_refptr<X509Certificate> puny_cert(cert_list[0]);

  EXPECT_EQ(NSSCertDatabase::TRUSTED_SSL,
            cert_db_->GetCertTrust(puny_cert.get(), SERVER_CERT));
  EXPECT_EQ(unsigned(CERTDB_TRUSTED | CERTDB_TERMINAL_RECORD),
            puny_cert->os_cert_handle()->trust->sslFlags);

  scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
  int flags = 0;
  CertVerifyResult verify_result;
  int error = verify_proc->Verify(puny_cert, "xn--wgv71a119e.com", flags,
                                  NULL, &verify_result);
  EXPECT_EQ(OK, error);
  EXPECT_EQ(0U, verify_result.cert_status);
}

TEST_F(CertDatabaseNSSTest, ImportCaAndServerCert) {
  CertificateList ca_certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "root_ca_cert.crt",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, ca_certs.size());

  // Import CA cert and trust it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::TRUSTED_SSL,
                                      &failed));
  EXPECT_EQ(0U, failed.size());

  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "ok_cert.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());

  // Import server cert with default trust.
  EXPECT_TRUE(cert_db_->ImportServerCert(certs, NSSCertDatabase::TRUST_DEFAULT,
                                         &failed));
  EXPECT_EQ(0U, failed.size());

  // Server cert should verify.
  scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
  int flags = 0;
  CertVerifyResult verify_result;
  int error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                                  NULL, &verify_result);
  EXPECT_EQ(OK, error);
  EXPECT_EQ(0U, verify_result.cert_status);
}

TEST_F(CertDatabaseNSSTest, ImportCaAndServerCert_DistrustServer) {
  // Explicit distrust only works starting in NSS 3.13.
  if (!NSS_VersionCheck("3.13")) {
    LOG(INFO) << "test skipped on NSS < 3.13";
    return;
  }

  CertificateList ca_certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "root_ca_cert.crt",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, ca_certs.size());

  // Import CA cert and trust it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::TRUSTED_SSL,
                                      &failed));
  EXPECT_EQ(0U, failed.size());

  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "ok_cert.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());

  // Import server cert without inheriting trust from issuer (explicit
  // distrust).
  EXPECT_TRUE(cert_db_->ImportServerCert(
      certs, NSSCertDatabase::DISTRUSTED_SSL, &failed));
  EXPECT_EQ(0U, failed.size());
  EXPECT_EQ(NSSCertDatabase::DISTRUSTED_SSL,
            cert_db_->GetCertTrust(certs[0], SERVER_CERT));

  EXPECT_EQ(unsigned(CERTDB_TERMINAL_RECORD),
            certs[0]->os_cert_handle()->trust->sslFlags);

  // Server cert should fail to verify.
  scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
  int flags = 0;
  CertVerifyResult verify_result;
  int error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                                  NULL, &verify_result);
  EXPECT_EQ(ERR_CERT_REVOKED, error);
  EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status);
}

TEST_F(CertDatabaseNSSTest, TrustIntermediateCa) {
  CertificateList ca_certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-root.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, ca_certs.size());

  // Import Root CA cert and distrust it.
  NSSCertDatabase::ImportCertFailureList failed;
  EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::DISTRUSTED_SSL,
                                      &failed));
  EXPECT_EQ(0U, failed.size());

  CertificateList intermediate_certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-intermediate.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, intermediate_certs.size());

  // Import Intermediate CA cert and trust it.
  EXPECT_TRUE(cert_db_->ImportCACerts(intermediate_certs,
                                      NSSCertDatabase::TRUSTED_SSL, &failed));
  EXPECT_EQ(0U, failed.size());

  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-ee-by-2048-rsa-intermediate.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());

  // Import server cert with default trust.
  EXPECT_TRUE(cert_db_->ImportServerCert(
      certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
  EXPECT_EQ(0U, failed.size());
  EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
            cert_db_->GetCertTrust(certs[0], SERVER_CERT));

  // Server cert should verify.
  scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
  int flags = 0;
  CertVerifyResult verify_result;
  int error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                                  NULL, &verify_result);
  EXPECT_EQ(OK, error);
  EXPECT_EQ(0U, verify_result.cert_status);

  // Explicit distrust only works starting in NSS 3.13.
  if (!NSS_VersionCheck("3.13")) {
    LOG(INFO) << "test partially skipped on NSS < 3.13";
    return;
  }

  // Trust the root cert and distrust the intermediate.
  EXPECT_TRUE(cert_db_->SetCertTrust(
      ca_certs[0], CA_CERT, NSSCertDatabase::TRUSTED_SSL));
  EXPECT_TRUE(cert_db_->SetCertTrust(
      intermediate_certs[0], CA_CERT, NSSCertDatabase::DISTRUSTED_SSL));
  EXPECT_EQ(
      unsigned(CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA),
      ca_certs[0]->os_cert_handle()->trust->sslFlags);
  EXPECT_EQ(unsigned(CERTDB_VALID_CA),
            ca_certs[0]->os_cert_handle()->trust->emailFlags);
  EXPECT_EQ(unsigned(CERTDB_VALID_CA),
            ca_certs[0]->os_cert_handle()->trust->objectSigningFlags);
  EXPECT_EQ(unsigned(CERTDB_TERMINAL_RECORD),
            intermediate_certs[0]->os_cert_handle()->trust->sslFlags);
  EXPECT_EQ(unsigned(CERTDB_VALID_CA),
            intermediate_certs[0]->os_cert_handle()->trust->emailFlags);
  EXPECT_EQ(
      unsigned(CERTDB_VALID_CA),
      intermediate_certs[0]->os_cert_handle()->trust->objectSigningFlags);

  // Server cert should fail to verify.
  CertVerifyResult verify_result2;
  error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                              NULL, &verify_result2);
  EXPECT_EQ(ERR_CERT_REVOKED, error);
  EXPECT_EQ(CERT_STATUS_REVOKED, verify_result2.cert_status);
}

TEST_F(CertDatabaseNSSTest, TrustIntermediateCa2) {
  NSSCertDatabase::ImportCertFailureList failed;

  CertificateList intermediate_certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-intermediate.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, intermediate_certs.size());

  // Import Intermediate CA cert and trust it.
  EXPECT_TRUE(cert_db_->ImportCACerts(intermediate_certs,
                                      NSSCertDatabase::TRUSTED_SSL, &failed));
  EXPECT_EQ(0U, failed.size());

  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-ee-by-2048-rsa-intermediate.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());

  // Import server cert with default trust.
  EXPECT_TRUE(cert_db_->ImportServerCert(
      certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
  EXPECT_EQ(0U, failed.size());
  EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
            cert_db_->GetCertTrust(certs[0], SERVER_CERT));

  // Server cert should verify.
  scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
  int flags = 0;
  CertVerifyResult verify_result;
  int error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                                  NULL, &verify_result);
  EXPECT_EQ(OK, error);
  EXPECT_EQ(0U, verify_result.cert_status);

  // Without explicit trust of the intermediate, verification should fail.
  EXPECT_TRUE(cert_db_->SetCertTrust(
      intermediate_certs[0], CA_CERT, NSSCertDatabase::TRUST_DEFAULT));

  // Server cert should fail to verify.
  CertVerifyResult verify_result2;
  error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                              NULL, &verify_result2);
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
  EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status);
}

TEST_F(CertDatabaseNSSTest, TrustIntermediateCa3) {
  NSSCertDatabase::ImportCertFailureList failed;

  CertificateList ca_certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-root.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, ca_certs.size());

  // Import Root CA cert and default trust it.
  EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::TRUST_DEFAULT,
                                      &failed));
  EXPECT_EQ(0U, failed.size());

  CertificateList intermediate_certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-intermediate.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, intermediate_certs.size());

  // Import Intermediate CA cert and trust it.
  EXPECT_TRUE(cert_db_->ImportCACerts(intermediate_certs,
                                      NSSCertDatabase::TRUSTED_SSL, &failed));
  EXPECT_EQ(0U, failed.size());

  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-ee-by-2048-rsa-intermediate.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());

  // Import server cert with default trust.
  EXPECT_TRUE(cert_db_->ImportServerCert(
      certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
  EXPECT_EQ(0U, failed.size());
  EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
            cert_db_->GetCertTrust(certs[0], SERVER_CERT));

  // Server cert should verify.
  scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
  int flags = 0;
  CertVerifyResult verify_result;
  int error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                                  NULL, &verify_result);
  EXPECT_EQ(OK, error);
  EXPECT_EQ(0U, verify_result.cert_status);

  // Without explicit trust of the intermediate, verification should fail.
  EXPECT_TRUE(cert_db_->SetCertTrust(
      intermediate_certs[0], CA_CERT, NSSCertDatabase::TRUST_DEFAULT));

  // Server cert should fail to verify.
  CertVerifyResult verify_result2;
  error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                              NULL, &verify_result2);
  EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
  EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status);
}

TEST_F(CertDatabaseNSSTest, TrustIntermediateCa4) {
  // Explicit distrust only works starting in NSS 3.13.
  if (!NSS_VersionCheck("3.13")) {
    LOG(INFO) << "test skipped on NSS < 3.13";
    return;
  }

  NSSCertDatabase::ImportCertFailureList failed;

  CertificateList ca_certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-root.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, ca_certs.size());

  // Import Root CA cert and trust it.
  EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::TRUSTED_SSL,
                                      &failed));
  EXPECT_EQ(0U, failed.size());

  CertificateList intermediate_certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-intermediate.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, intermediate_certs.size());

  // Import Intermediate CA cert and distrust it.
  EXPECT_TRUE(cert_db_->ImportCACerts(
        intermediate_certs, NSSCertDatabase::DISTRUSTED_SSL, &failed));
  EXPECT_EQ(0U, failed.size());

  CertificateList certs = CreateCertificateListFromFile(
      GetTestCertsDirectory(), "2048-rsa-ee-by-2048-rsa-intermediate.pem",
      X509Certificate::FORMAT_AUTO);
  ASSERT_EQ(1U, certs.size());

  // Import server cert with default trust.
  EXPECT_TRUE(cert_db_->ImportServerCert(
      certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
  EXPECT_EQ(0U, failed.size());
  EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
            cert_db_->GetCertTrust(certs[0], SERVER_CERT));

  // Server cert should not verify.
  scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
  int flags = 0;
  CertVerifyResult verify_result;
  int error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                                  NULL, &verify_result);
  EXPECT_EQ(ERR_CERT_REVOKED, error);
  EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status);

  // Without explicit distrust of the intermediate, verification should succeed.
  EXPECT_TRUE(cert_db_->SetCertTrust(
      intermediate_certs[0], CA_CERT, NSSCertDatabase::TRUST_DEFAULT));

  // Server cert should verify.
  CertVerifyResult verify_result2;
  error = verify_proc->Verify(certs[0], "127.0.0.1", flags,
                              NULL, &verify_result2);
  EXPECT_EQ(OK, error);
  EXPECT_EQ(0U, verify_result2.cert_status);
}

}  // namespace net
