Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(160)

Unified Diff: net/cert/internal/trust_store_nss_unittest.cc

Issue 2126803004: WIP: NSS trust store integration for path builder. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@cert-command-line-path-builder-add_certpathbuilder
Patch Set: . Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/cert/internal/trust_store_nss.cc ('k') | net/cert/internal/trust_store_static.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « net/cert/internal/trust_store_nss.cc ('k') | net/cert/internal/trust_store_static.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698