| Index: net/cert/internal/trust_store_nss_unittest.cc
|
| diff --git a/net/cert/internal/trust_store_nss_unittest.cc b/net/cert/internal/trust_store_nss_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..28ce2ea3b484f23bd08235f9588a265b4e870a2c
|
| --- /dev/null
|
| +++ b/net/cert/internal/trust_store_nss_unittest.cc
|
| @@ -0,0 +1,431 @@
|
| +// Copyright 2016 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/internal/trust_store_nss.h"
|
| +
|
| +#include <cert.h>
|
| +#include <certdb.h>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/threading/thread_task_runner_handle.h"
|
| +#include "crypto/scoped_test_nss_db.h"
|
| +#include "net/cert/internal/test_helpers.h"
|
| +#include "net/cert/internal/trust_store_test_helpers.h"
|
| +#include "net/cert/scoped_nss_types.h"
|
| +#include "net/cert/x509_certificate.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +class TrustStoreNSSTest : public testing::Test {
|
| + public:
|
| + void SetUp() override {
|
| + ASSERT_TRUE(test_nssdb_.is_open());
|
| +
|
| + ParsedCertificateList chain;
|
| + ParsedCertificateList roots;
|
| + bool unused_verify_result;
|
| + der::GeneralizedTime unused_time;
|
| +
|
| + ReadCertChainTestFromFile(
|
| + "net/data/verify_certificate_chain_unittest/key-rollover-oldchain.pem",
|
| + &chain, &roots, &unused_time, &unused_verify_result);
|
| + ASSERT_EQ(2U, chain.size());
|
| + ASSERT_EQ(1U, roots.size());
|
| + target_ = chain[0];
|
| + oldintermediate_ = chain[1];
|
| + oldroot_ = roots[0];
|
| + ASSERT_TRUE(target_);
|
| + ASSERT_TRUE(oldintermediate_);
|
| + ASSERT_TRUE(oldroot_);
|
| +
|
| + ReadCertChainTestFromFile(
|
| + "net/data/verify_certificate_chain_unittest/"
|
| + "key-rollover-longrolloverchain.pem",
|
| + &chain, &roots, &unused_time, &unused_verify_result);
|
| + ASSERT_EQ(4U, chain.size());
|
| + newintermediate_ = chain[1];
|
| + newroot_ = chain[2];
|
| + newrootrollover_ = chain[3];
|
| + ASSERT_TRUE(newintermediate_);
|
| + ASSERT_TRUE(newroot_);
|
| + ASSERT_TRUE(newrootrollover_);
|
| +
|
| + trust_store_nss_.reset(
|
| + new TrustStoreNSS(base::ThreadTaskRunnerHandle::Get()));
|
| + }
|
| +
|
| + std::string GetUniqueNickname() {
|
| + return "trust_store_nss_unittest" + base::UintToString(nickname_counter_++);
|
| + }
|
| +
|
| + void AddCertToNSS(const ParsedCertificate* cert) {
|
| + std::string nickname = GetUniqueNickname();
|
| + ScopedCERTCertificate nss_cert(
|
| + X509Certificate::CreateOSCertHandleFromBytesWithNickname(
|
| + cert->der_cert().AsStringPiece().data(), cert->der_cert().Length(),
|
| + nickname.c_str()));
|
| + ASSERT_TRUE(nss_cert);
|
| + SECStatus srv =
|
| + PK11_ImportCert(test_nssdb_.slot(), nss_cert.get(), CK_INVALID_HANDLE,
|
| + nickname.c_str(), PR_FALSE /* includeTrust (unused) */);
|
| + ASSERT_EQ(SECSuccess, srv);
|
| + }
|
| +
|
| + void AddCertsToNSS() {
|
| + AddCertToNSS(target_.get());
|
| + AddCertToNSS(oldintermediate_.get());
|
| + AddCertToNSS(newintermediate_.get());
|
| + AddCertToNSS(oldroot_.get());
|
| + AddCertToNSS(newroot_.get());
|
| + AddCertToNSS(newrootrollover_.get());
|
| + }
|
| +
|
| + // Trusts |cert|. Assumes the cert was already imported into NSS.
|
| + void TrustCert(const ParsedCertificate* cert) {
|
| + SECItem der_cert;
|
| + der_cert.data = const_cast<uint8_t*>(cert->der_cert().UnsafeData());
|
| + der_cert.len = base::checked_cast<unsigned>(cert->der_cert().Length());
|
| + der_cert.type = siDERCertBuffer;
|
| +
|
| + // XXX Is this an acceptable way to get the cert for checking trust? Should it
|
| + // use CERT_NewTempCertificate instead? Or is there a way to get a trust value
|
| + // directly without going through CERT_GetCertTrust?
|
| + ScopedCERTCertificate nss_cert(
|
| + CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), &der_cert));
|
| + ASSERT_TRUE(nss_cert);
|
| +
|
| + CERTCertTrust trust = {0};
|
| + trust.sslFlags =
|
| + CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA | CERTDB_VALID_CA;
|
| + SECStatus srv =
|
| + CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nss_cert.get(), &trust);
|
| + ASSERT_EQ(SECSuccess, srv);
|
| + }
|
| +
|
| + bool IsTrusted(scoped_refptr<ParsedCertificate> cert) {
|
| + bool unused_trusted = false;
|
| + TrustResultRecorder trust_results;
|
| + std::unique_ptr<TrustStore::Request> trust_req;
|
| + trust_store_nss_->IsTrustedCertificate(std::move(cert), trust_results.Callback(),
|
| + &unused_trusted, &trust_req);
|
| + EXPECT_FALSE(unused_trusted);
|
| + if (!trust_req) {
|
| + ADD_FAILURE() << "!trust_req";
|
| + return false;
|
| + }
|
| + trust_results.Run();
|
| + if (trust_results.results().size() != 1U) {
|
| + ADD_FAILURE() << "trust_results.results().size() == "
|
| + << trust_results.results().size();
|
| + return false;
|
| + }
|
| + return trust_results.results()[0];
|
| + }
|
| +
|
| + protected:
|
| + scoped_refptr<ParsedCertificate> target_;
|
| + scoped_refptr<ParsedCertificate> oldintermediate_;
|
| + scoped_refptr<ParsedCertificate> newintermediate_;
|
| + scoped_refptr<ParsedCertificate> oldroot_;
|
| + scoped_refptr<ParsedCertificate> newroot_;
|
| + scoped_refptr<ParsedCertificate> newrootrollover_;
|
| + crypto::ScopedTestNSSDB test_nssdb_;
|
| + std::unique_ptr<TrustStoreNSS> trust_store_nss_;
|
| + unsigned nickname_counter_ = 0;
|
| +};
|
| +
|
| +class IssuerResultRecorder {
|
| + public:
|
| + IssuerResultRecorder() {
|
| + run_loops_.push_back(base::WrapUnique(new base::RunLoop()));
|
| + }
|
| +
|
| + void HandleResult(CertIssuerSource::Request* req) {
|
| + results_.push_back(req);
|
| + run_loops_.back()->Quit();
|
| + run_loops_.push_back(base::WrapUnique(new base::RunLoop()));
|
| + }
|
| +
|
| + const std::vector<CertIssuerSource::Request*>& results() { return results_; }
|
| +
|
| + CertIssuerSource::Request* Run() {
|
| + run_loops_[cur_]->Run();
|
| + return results_[cur_++];
|
| + }
|
| +
|
| + CertIssuerSource::IssuerCallback Callback() {
|
| + return base::Bind(&IssuerResultRecorder::HandleResult, base::Unretained(this));
|
| + }
|
| +
|
| + private:
|
| + std::vector<CertIssuerSource::Request*> results_;
|
| + std::vector<std::unique_ptr<base::RunLoop>> run_loops_;
|
| + size_t cur_ = 0;
|
| +};
|
| +
|
| +void IssuerRequestDeleter(std::unique_ptr<CertIssuerSource::Request>* req_owner,
|
| + base::Closure done_callback,
|
| + CertIssuerSource::Request* req) {
|
| + ASSERT_EQ(req, req_owner->get());
|
| + req_owner->reset();
|
| + done_callback.Run();
|
| +}
|
| +
|
| +// Without adding the test certs to the NSS DB, should get no issuer results and
|
| +// none of the certs should be trusted.
|
| +TEST_F(TrustStoreNSSTest, CertsNotPresent) {
|
| + ParsedCertificateList issuers;
|
| + trust_store_nss_->SyncGetIssuersOf(target_.get(), &issuers);
|
| + EXPECT_TRUE(issuers.empty());
|
| +
|
| + IssuerResultRecorder issuer_results;
|
| + std::unique_ptr<CertIssuerSource::Request> req;
|
| + trust_store_nss_->AsyncGetIssuersOf(target_,
|
| + issuer_results.Callback(), &req);
|
| + ASSERT_TRUE(req);
|
| + CertIssuerSource::Request* done_req = issuer_results.Run();
|
| + EXPECT_EQ(done_req, req.get());
|
| + scoped_refptr<ParsedCertificate> result_cert;
|
| + EXPECT_EQ(CompletionStatus::SYNC, req->GetNext(&result_cert));
|
| + EXPECT_FALSE(result_cert);
|
| +
|
| + EXPECT_FALSE(IsTrusted(oldroot_));
|
| + EXPECT_FALSE(IsTrusted(newrootrollover_));
|
| + EXPECT_FALSE(IsTrusted(newroot_));
|
| + EXPECT_FALSE(IsTrusted(oldintermediate_));
|
| + EXPECT_FALSE(IsTrusted(newintermediate_));
|
| + EXPECT_FALSE(IsTrusted(target_));
|
| +}
|
| +
|
| +// Certs added to the NSS DB, but not trusted. Should get issuer matches, but
|
| +// nothing should be marked trusted.
|
| +TEST_F(TrustStoreNSSTest, UntrustedIntermediates) {
|
| + AddCertsToNSS();
|
| +
|
| + // Request issuers of the target cert. TrustStoreNSS never returns results
|
| + // synchronously.
|
| + ParsedCertificateList issuers;
|
| + trust_store_nss_->SyncGetIssuersOf(target_.get(), &issuers);
|
| + EXPECT_TRUE(issuers.empty());
|
| +
|
| + // Async get should return both issuers, even though they aren't trusted they
|
| + // can be used for path building.
|
| + IssuerResultRecorder issuer_results;
|
| + std::unique_ptr<CertIssuerSource::Request> issuer_req;
|
| + trust_store_nss_->AsyncGetIssuersOf(target_,
|
| + issuer_results.Callback(), &issuer_req);
|
| + ASSERT_TRUE(issuer_req);
|
| + CertIssuerSource::Request* done_issuer_req = issuer_results.Run();
|
| + EXPECT_EQ(done_issuer_req, issuer_req.get());
|
| + scoped_refptr<ParsedCertificate> result_cert1;
|
| + ASSERT_EQ(CompletionStatus::SYNC, issuer_req->GetNext(&result_cert1));
|
| + ASSERT_TRUE(result_cert1);
|
| + scoped_refptr<ParsedCertificate> result_cert2;
|
| + ASSERT_EQ(CompletionStatus::SYNC, issuer_req->GetNext(&result_cert2));
|
| + ASSERT_TRUE(result_cert2);
|
| + scoped_refptr<ParsedCertificate> result_cert3;
|
| + EXPECT_EQ(CompletionStatus::SYNC, issuer_req->GetNext(&result_cert3));
|
| + EXPECT_FALSE(result_cert3);
|
| +
|
| + if (result_cert1->der_cert() == oldintermediate_->der_cert()) {
|
| + EXPECT_EQ(result_cert2->der_cert(), newintermediate_->der_cert());
|
| + } else {
|
| + EXPECT_EQ(result_cert1->der_cert(), newintermediate_->der_cert());
|
| + EXPECT_EQ(result_cert2->der_cert(), oldintermediate_->der_cert());
|
| + }
|
| +
|
| + // None of the certs are trusted.
|
| + EXPECT_FALSE(IsTrusted(oldroot_));
|
| + EXPECT_FALSE(IsTrusted(newrootrollover_));
|
| + EXPECT_FALSE(IsTrusted(newroot_));
|
| + EXPECT_FALSE(IsTrusted(oldintermediate_));
|
| + EXPECT_FALSE(IsTrusted(newintermediate_));
|
| + EXPECT_FALSE(IsTrusted(target_));
|
| +}
|
| +
|
| +// Certs added to the NSS DB, and one of the CA certs trusted.
|
| +// Should get issuer matches, and that one cert should return as trusted.
|
| +TEST_F(TrustStoreNSSTest, TrustedCA) {
|
| + AddCertsToNSS();
|
| + TrustCert(newroot_.get());
|
| + ParsedCertificateList issuers;
|
| +
|
| + // Getting issuers of the intermediate should return all the matching roots in
|
| + // NSS DB, regardless if they were trusted.
|
| + IssuerResultRecorder issuer_results;
|
| + std::unique_ptr<CertIssuerSource::Request> req;
|
| + trust_store_nss_->AsyncGetIssuersOf(oldintermediate_,
|
| + issuer_results.Callback(), &req);
|
| + ASSERT_TRUE(req);
|
| + CertIssuerSource::Request* done_req = issuer_results.Run();
|
| + EXPECT_EQ(done_req, req.get());
|
| + scoped_refptr<ParsedCertificate> result_cert1;
|
| + ASSERT_EQ(CompletionStatus::SYNC, req->GetNext(&result_cert1));
|
| + ASSERT_TRUE(result_cert1);
|
| + scoped_refptr<ParsedCertificate> result_cert2;
|
| + ASSERT_EQ(CompletionStatus::SYNC, req->GetNext(&result_cert2));
|
| + ASSERT_TRUE(result_cert2);
|
| + scoped_refptr<ParsedCertificate> result_cert3;
|
| + ASSERT_EQ(CompletionStatus::SYNC, req->GetNext(&result_cert3));
|
| + ASSERT_TRUE(result_cert3);
|
| + scoped_refptr<ParsedCertificate> result_cert4;
|
| + EXPECT_EQ(CompletionStatus::SYNC, req->GetNext(&result_cert4));
|
| + EXPECT_FALSE(result_cert4);
|
| +
|
| + std::vector<base::StringPiece> expected_issuers = {
|
| + oldroot_->der_cert().AsStringPiece(),
|
| + newroot_->der_cert().AsStringPiece(),
|
| + newrootrollover_->der_cert().AsStringPiece(),
|
| + };
|
| + std::vector<base::StringPiece> got_issuers = {
|
| + result_cert1->der_cert().AsStringPiece(),
|
| + result_cert2->der_cert().AsStringPiece(),
|
| + result_cert3->der_cert().AsStringPiece(),
|
| + };
|
| + std::sort(expected_issuers.begin(), expected_issuers.end());
|
| + std::sort(got_issuers.begin(), got_issuers.end());
|
| + EXPECT_EQ(got_issuers, expected_issuers);
|
| +
|
| + // Only newroot is trusted.
|
| + EXPECT_FALSE(IsTrusted(oldroot_));
|
| + EXPECT_FALSE(IsTrusted(newrootrollover_));
|
| + EXPECT_TRUE(IsTrusted(newroot_));
|
| + EXPECT_FALSE(IsTrusted(oldintermediate_));
|
| + EXPECT_FALSE(IsTrusted(newintermediate_));
|
| + EXPECT_FALSE(IsTrusted(target_));
|
| +}
|
| +
|
| +// Cancel a AsyncGetIssuersOf request before it has returned any results.
|
| +// Callback should not be called.
|
| +TEST_F(TrustStoreNSSTest, CancelIssuerRequest) {
|
| + IssuerResultRecorder issuer_results;
|
| + std::unique_ptr<CertIssuerSource::Request> req;
|
| + trust_store_nss_->AsyncGetIssuersOf(target_,
|
| + issuer_results.Callback(), &req);
|
| + ASSERT_TRUE(req);
|
| + req.reset();
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_TRUE(issuer_results.results().empty());
|
| +}
|
| +
|
| +// Cancel a AsyncGetIssuersOf request after the callback was called, but during
|
| +// the middle of retrieving the results with GetNext. Mostly just tests that
|
| +// nothing crashes.
|
| +TEST_F(TrustStoreNSSTest, CancelIssuerRequestDuringResults) {
|
| + AddCertsToNSS();
|
| +
|
| + IssuerResultRecorder issuer_results;
|
| + std::unique_ptr<CertIssuerSource::Request> issuer_req;
|
| + trust_store_nss_->AsyncGetIssuersOf(target_,
|
| + issuer_results.Callback(), &issuer_req);
|
| + ASSERT_TRUE(issuer_req);
|
| + CertIssuerSource::Request* done_issuer_req = issuer_results.Run();
|
| + EXPECT_EQ(done_issuer_req, issuer_req.get());
|
| + scoped_refptr<ParsedCertificate> result_cert1;
|
| + ASSERT_EQ(CompletionStatus::SYNC, issuer_req->GetNext(&result_cert1));
|
| + ASSERT_TRUE(result_cert1);
|
| +
|
| + issuer_req.reset();
|
| + base::RunLoop().RunUntilIdle();
|
| +}
|
| +
|
| +// Cancel a AsyncGetIssuersOf request during the callback. Should not crash.
|
| +TEST_F(TrustStoreNSSTest, CancelIssuerRequestDuringCallback) {
|
| + AddCertsToNSS();
|
| +
|
| + base::RunLoop run_loop;
|
| + std::unique_ptr<CertIssuerSource::Request> issuer_req;
|
| + trust_store_nss_->AsyncGetIssuersOf(
|
| + target_,
|
| + base::Bind(&IssuerRequestDeleter, &issuer_req, run_loop.QuitClosure()),
|
| + &issuer_req);
|
| + ASSERT_TRUE(issuer_req);
|
| + run_loop.Run();
|
| + ASSERT_FALSE(issuer_req);
|
| + base::RunLoop().RunUntilIdle();
|
| +}
|
| +
|
| +// Cancel a IsTrustedCertificate request before it has returned any results.
|
| +// Callback should not be called.
|
| +TEST_F(TrustStoreNSSTest, CancelTrustRequest) {
|
| + bool unused_trusted = false;
|
| + std::unique_ptr<TrustStore::Request> trust_req;
|
| + TrustResultRecorder trust_results;
|
| + trust_store_nss_->IsTrustedCertificate(newroot_, trust_results.Callback(),
|
| + &unused_trusted, &trust_req);
|
| + ASSERT_TRUE(trust_req);
|
| + trust_req.reset();
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_TRUE(trust_results.results().empty());
|
| +}
|
| +
|
| +// Cancel a IsTrustedCertificate request during the callback. Should not crash.
|
| +TEST_F(TrustStoreNSSTest, CancelTrustRequestDuringCallback) {
|
| + bool unused_trusted = false;
|
| + base::RunLoop run_loop;
|
| + std::unique_ptr<TrustStore::Request> trust_req;
|
| + trust_store_nss_->IsTrustedCertificate(
|
| + newroot_,
|
| + base::Bind(&TrustRequestDeleter, &trust_req, run_loop.QuitClosure()),
|
| + &unused_trusted, &trust_req);
|
| + ASSERT_TRUE(trust_req);
|
| + run_loop.Run();
|
| + ASSERT_FALSE(trust_req);
|
| + base::RunLoop().RunUntilIdle();
|
| +}
|
| +
|
| +
|
| +// XXX this test fails
|
| +TEST_F(TrustStoreNSSTest, Normalziatison) {
|
| + ParsedCertificateList chain;
|
| + ParsedCertificateList roots;
|
| + bool unused_verify_result;
|
| + der::GeneralizedTime unused_time;
|
| +
|
| + ReadCertChainTestFromFile(
|
| + "net/data/verify_certificate_chain_unittest/"
|
| + "issuer-and-subject-not-byte-for-byte-equal-anchor.pem",
|
| + &chain, &roots, &unused_time, &unused_verify_result);
|
| + ASSERT_EQ(2U, chain.size());
|
| + ASSERT_EQ(1U, roots.size());
|
| + scoped_refptr<ParsedCertificate> target = chain[0];
|
| + scoped_refptr<ParsedCertificate> intermediate = chain[1];
|
| + scoped_refptr<ParsedCertificate> root = roots[0];
|
| + ASSERT_TRUE(target);
|
| + ASSERT_TRUE(intermediate);
|
| + ASSERT_TRUE(root);
|
| +
|
| + EXPECT_NE(intermediate->tbs().issuer_tlv, root->tbs().subject_tlv);
|
| + EXPECT_EQ(intermediate->normalized_issuer(), root->normalized_subject());
|
| +
|
| + AddCertToNSS(root.get());
|
| +
|
| + // Async get should return both issuers, even though they aren't trusted they
|
| + // can be used for path building.
|
| + IssuerResultRecorder issuer_results;
|
| + std::unique_ptr<CertIssuerSource::Request> issuer_req;
|
| + trust_store_nss_->AsyncGetIssuersOf(intermediate, issuer_results.Callback(),
|
| + &issuer_req);
|
| + ASSERT_TRUE(issuer_req);
|
| + CertIssuerSource::Request* done_issuer_req = issuer_results.Run();
|
| + EXPECT_EQ(done_issuer_req, issuer_req.get());
|
| + scoped_refptr<ParsedCertificate> result_cert1;
|
| + ASSERT_EQ(CompletionStatus::SYNC, issuer_req->GetNext(&result_cert1));
|
| + ASSERT_TRUE(result_cert1);
|
| + scoped_refptr<ParsedCertificate> result_cert2;
|
| + EXPECT_EQ(CompletionStatus::SYNC, issuer_req->GetNext(&result_cert2));
|
| + EXPECT_FALSE(result_cert2);
|
| +
|
| + EXPECT_EQ(result_cert1->der_cert(), root->der_cert());
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +} // namespace net
|
|
|