blob: 618356d4390d4fa979a049b185426c7e203b213b [file] [log] [blame]
// Copyright 2014 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 "services/network/cert_verifier_with_trust_anchors.h"
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "crypto/nss_util_internal.h"
#include "crypto/scoped_test_nss_chromeos_user.h"
#include "net/base/test_completion_callback.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/nss_cert_database_chromeos.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util_nss.h"
#include "net/log/net_log_with_source.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "services/network/cert_verify_proc_chromeos.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
class CertVerifierWithTrustAnchorsTest : public testing::Test {
public:
CertVerifierWithTrustAnchorsTest()
: trust_anchor_used_(false),
test_nss_user_("user1"),
scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
~CertVerifierWithTrustAnchorsTest() override {}
void SetUp() override {
ASSERT_TRUE(test_nss_user_.constructed_successfully());
test_nss_user_.FinishInit();
test_cert_db_.reset(new net::NSSCertDatabaseChromeOS(
crypto::GetPublicSlotForChromeOSUser(test_nss_user_.username_hash()),
crypto::GetPrivateSlotForChromeOSUser(
test_nss_user_.username_hash(),
base::RepeatingCallback<void(crypto::ScopedPK11Slot)>())));
cert_verifier_.reset(new CertVerifierWithTrustAnchors(base::BindRepeating(
&CertVerifierWithTrustAnchorsTest::OnTrustAnchorUsed,
base::Unretained(this))));
cert_verifier_->InitializeOnIOThread(new network::CertVerifyProcChromeOS(
crypto::GetPublicSlotForChromeOSUser(test_nss_user_.username_hash())));
test_ca_cert_ = LoadCertificate("root_ca_cert.pem", net::CA_CERT);
ASSERT_TRUE(test_ca_cert_);
test_ca_cert_list_.push_back(
net::x509_util::DupCERTCertificate(test_ca_cert_.get()));
net::ScopedCERTCertificate test_server_cert =
LoadCertificate("ok_cert.pem", net::SERVER_CERT);
ASSERT_TRUE(test_server_cert);
test_server_cert_ =
net::x509_util::CreateX509CertificateFromCERTCertificate(
test_server_cert.get());
ASSERT_TRUE(test_server_cert_);
}
void TearDown() override {
// Destroy |cert_verifier_| before destroying the ThreadBundle, otherwise
// BrowserThread::CurrentlyOn checks fail.
cert_verifier_.reset();
}
protected:
int VerifyTestServerCert(
const net::TestCompletionCallback& test_callback,
net::CertVerifyResult* verify_result,
std::unique_ptr<net::CertVerifier::Request>* request) {
return cert_verifier_->Verify(
net::CertVerifier::RequestParams(test_server_cert_.get(), "127.0.0.1",
0, std::string()),
verify_result, test_callback.callback(), request,
net::NetLogWithSource());
}
bool SupportsAdditionalTrustAnchors() {
scoped_refptr<net::CertVerifyProc> proc =
net::CertVerifyProc::CreateDefault();
return proc->SupportsAdditionalTrustAnchors();
}
// Returns whether |cert_verifier| signalled usage of one of the additional
// trust anchors (i.e. of |test_ca_cert_|) for the first time or since the
// last call of this function.
bool WasTrustAnchorUsedAndReset() {
base::RunLoop().RunUntilIdle();
bool result = trust_anchor_used_;
trust_anchor_used_ = false;
return result;
}
// |test_ca_cert_| is the issuer of |test_server_cert_|.
net::ScopedCERTCertificate test_ca_cert_;
scoped_refptr<net::X509Certificate> test_server_cert_;
net::ScopedCERTCertificateList test_ca_cert_list_;
std::unique_ptr<net::NSSCertDatabaseChromeOS> test_cert_db_;
std::unique_ptr<CertVerifierWithTrustAnchors> cert_verifier_;
private:
void OnTrustAnchorUsed() { trust_anchor_used_ = true; }
net::ScopedCERTCertificate LoadCertificate(const std::string& name,
net::CertType type) {
net::ScopedCERTCertificate cert =
net::ImportCERTCertificateFromFile(net::GetTestCertsDirectory(), name);
if (!cert)
return cert;
// No certificate is trusted right after it's loaded.
net::NSSCertDatabase::TrustBits trust =
test_cert_db_->GetCertTrust(cert.get(), type);
EXPECT_EQ(net::NSSCertDatabase::TRUST_DEFAULT, trust);
return cert;
}
bool trust_anchor_used_;
crypto::ScopedTestNSSChromeOSUser test_nss_user_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
};
TEST_F(CertVerifierWithTrustAnchorsTest, VerifyUntrustedCert) {
// |test_server_cert_| is untrusted, so Verify() fails.
{
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
ASSERT_EQ(net::ERR_IO_PENDING, error);
EXPECT_TRUE(request);
error = callback.WaitForResult();
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
}
// Issuing the same request again hits the cache. This tests the synchronous
// path.
{
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
}
EXPECT_FALSE(WasTrustAnchorUsedAndReset());
}
TEST_F(CertVerifierWithTrustAnchorsTest, VerifyTrustedCert) {
// Make the database trust |test_ca_cert_|.
net::NSSCertDatabase::ImportCertFailureList failure_list;
ASSERT_TRUE(test_cert_db_->ImportCACerts(
test_ca_cert_list_, net::NSSCertDatabase::TRUSTED_SSL, &failure_list));
ASSERT_TRUE(failure_list.empty());
// Verify that it is now trusted.
net::NSSCertDatabase::TrustBits trust =
test_cert_db_->GetCertTrust(test_ca_cert_.get(), net::CA_CERT);
EXPECT_EQ(net::NSSCertDatabase::TRUSTED_SSL, trust);
// Verify() successfully verifies |test_server_cert_| after it was imported.
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
ASSERT_EQ(net::ERR_IO_PENDING, error);
EXPECT_TRUE(request);
error = callback.WaitForResult();
EXPECT_EQ(net::OK, error);
// The additional trust anchors were not used, since the certificate is
// trusted from the database.
EXPECT_FALSE(WasTrustAnchorUsedAndReset());
}
TEST_F(CertVerifierWithTrustAnchorsTest, VerifyUsingAdditionalTrustAnchor) {
ASSERT_TRUE(SupportsAdditionalTrustAnchors());
// |test_server_cert_| is untrusted, so Verify() fails.
{
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
ASSERT_EQ(net::ERR_IO_PENDING, error);
EXPECT_TRUE(request);
error = callback.WaitForResult();
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
}
EXPECT_FALSE(WasTrustAnchorUsedAndReset());
net::CertificateList test_ca_x509cert_list =
net::x509_util::CreateX509CertificateListFromCERTCertificates(
test_ca_cert_list_);
ASSERT_FALSE(test_ca_x509cert_list.empty());
// Verify() again with the additional trust anchors.
cert_verifier_->SetTrustAnchors(test_ca_x509cert_list);
{
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
ASSERT_EQ(net::ERR_IO_PENDING, error);
EXPECT_TRUE(request);
error = callback.WaitForResult();
EXPECT_EQ(net::OK, error);
}
EXPECT_TRUE(WasTrustAnchorUsedAndReset());
// Verify() again with the additional trust anchors will hit the cache.
cert_verifier_->SetTrustAnchors(test_ca_x509cert_list);
{
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
EXPECT_EQ(net::OK, error);
}
EXPECT_TRUE(WasTrustAnchorUsedAndReset());
// Verifying after removing the trust anchors should now fail.
cert_verifier_->SetTrustAnchors(net::CertificateList());
{
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
// Note: Changing the trust anchors should flush the cache.
ASSERT_EQ(net::ERR_IO_PENDING, error);
EXPECT_TRUE(request);
error = callback.WaitForResult();
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
}
// The additional trust anchors were reset, thus |cert_verifier_| should not
// signal it's usage anymore.
EXPECT_FALSE(WasTrustAnchorUsedAndReset());
}
TEST_F(CertVerifierWithTrustAnchorsTest,
VerifyUsesAdditionalTrustAnchorsAfterConfigChange) {
ASSERT_TRUE(SupportsAdditionalTrustAnchors());
// |test_server_cert_| is untrusted, so Verify() fails.
{
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
ASSERT_EQ(net::ERR_IO_PENDING, error);
EXPECT_TRUE(request);
error = callback.WaitForResult();
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
}
EXPECT_FALSE(WasTrustAnchorUsedAndReset());
net::CertificateList test_ca_x509cert_list =
net::x509_util::CreateX509CertificateListFromCERTCertificates(
test_ca_cert_list_);
ASSERT_FALSE(test_ca_x509cert_list.empty());
// Verify() again with the additional trust anchors.
cert_verifier_->SetTrustAnchors(test_ca_x509cert_list);
{
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
ASSERT_EQ(net::ERR_IO_PENDING, error);
EXPECT_TRUE(request);
error = callback.WaitForResult();
EXPECT_EQ(net::OK, error);
}
EXPECT_TRUE(WasTrustAnchorUsedAndReset());
// Change the configuration to enable SHA-1, which should still use the
// additional trust anchors.
net::CertVerifier::Config config;
config.enable_sha1_local_anchors = true;
cert_verifier_->SetConfig(config);
{
net::CertVerifyResult verify_result;
net::TestCompletionCallback callback;
std::unique_ptr<net::CertVerifier::Request> request;
int error = VerifyTestServerCert(callback, &verify_result, &request);
ASSERT_EQ(net::ERR_IO_PENDING, error);
EXPECT_TRUE(request);
error = callback.WaitForResult();
EXPECT_EQ(net::OK, error);
}
EXPECT_TRUE(WasTrustAnchorUsedAndReset());
}
} // namespace network