blob: 65797192157ac07b30245d7d8772c7472a09ead2 [file] [log] [blame]
// 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 {
void NotCalled(TrustAnchors anchors) {
ADD_FAILURE() << "NotCalled was called";
}
class TrustStoreNSSTest : public testing::Test {
public:
void SetUp() override {
ASSERT_TRUE(test_nssdb_.is_open());
ParsedCertificateList chain;
bool unused_verify_result;
der::GeneralizedTime unused_time;
std::string unused_errors;
ReadVerifyCertChainTestFromFile("key-rollover-oldchain.pem", &chain,
&oldroot_, &unused_time,
&unused_verify_result, &unused_errors);
ASSERT_EQ(2U, chain.size());
target_ = chain[0];
oldintermediate_ = chain[1];
ASSERT_TRUE(target_);
ASSERT_TRUE(oldintermediate_);
ASSERT_TRUE(oldroot_);
scoped_refptr<TrustAnchor> unused_root;
ReadVerifyCertChainTestFromFile("key-rollover-longrolloverchain.pem",
&chain, &unused_root, &unused_time,
&unused_verify_result, &unused_errors);
ASSERT_EQ(4U, chain.size());
newintermediate_ = chain[1];
newroot_ = TrustAnchor::CreateFromCertificateNoConstraints(chain[2]);
newrootrollover_ = chain[3];
ASSERT_TRUE(newintermediate_);
ASSERT_TRUE(newroot_);
ASSERT_TRUE(newrootrollover_);
trust_store_nss_.reset(
new TrustStoreNSS(trustSSL, 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_->cert().get());
AddCertToNSS(newroot_->cert().get());
AddCertToNSS(newrootrollover_.get());
}
// Trusts |cert|. Assumes the cert was already imported into NSS.
void TrustCert(const TrustAnchor* anchor) { TrustCert(anchor->cert().get()); }
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;
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);
}
protected:
void ExpectTrustStoreContains(tracked_objects::Location loc,
scoped_refptr<ParsedCertificate> cert,
TrustAnchors expected_async_matches) {
SCOPED_TRACE(loc.ToString());
TrustAnchors sync_matches;
TrustAnchorResultRecorder anchor_results;
std::unique_ptr<TrustStore::Request> req;
trust_store_nss_->FindTrustAnchorsForCert(cert, anchor_results.Callback(),
&sync_matches, &req);
ASSERT_TRUE(req);
EXPECT_TRUE(sync_matches.empty());
anchor_results.Run();
std::vector<der::Input> der_result_matches;
for (const auto& it : anchor_results.matches())
der_result_matches.push_back(it->cert()->der_cert());
std::sort(der_result_matches.begin(), der_result_matches.end());
std::vector<der::Input> der_expected_matches;
for (const auto& it : expected_async_matches)
der_expected_matches.push_back(it->cert()->der_cert());
std::sort(der_expected_matches.begin(), der_expected_matches.end());
EXPECT_EQ(der_expected_matches, der_result_matches);
}
scoped_refptr<TrustAnchor> oldroot_;
scoped_refptr<TrustAnchor> newroot_;
scoped_refptr<ParsedCertificate> target_;
scoped_refptr<ParsedCertificate> oldintermediate_;
scoped_refptr<ParsedCertificate> newintermediate_;
scoped_refptr<ParsedCertificate> newrootrollover_;
crypto::ScopedTestNSSDB test_nssdb_;
std::unique_ptr<TrustStoreNSS> trust_store_nss_;
unsigned nickname_counter_ = 0;
};
// Without adding any certs to the NSS DB, should get no anchor results for any
// of the test certs.
TEST_F(TrustStoreNSSTest, CertsNotPresent) {
ExpectTrustStoreContains(FROM_HERE, target_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, newintermediate_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, newroot_->cert(), TrustAnchors());
}
// If certs are present in NSS DB but aren't marked as trusted, should get no
// anchor results for any of the test certs.
TEST_F(TrustStoreNSSTest, CertsPresentButNotTrusted) {
AddCertsToNSS();
ExpectTrustStoreContains(FROM_HERE, newintermediate_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, target_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, newintermediate_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, newroot_->cert(), TrustAnchors());
}
// A self-signed CA certificate is trusted. FindTrustAnchorsForCert should
// return the cert on any intermediates with a matching issuer, and on any
// matching self-signed/self-issued CA certs.
TEST_F(TrustStoreNSSTest, TrustedCA) {
AddCertsToNSS();
TrustCert(newroot_.get());
ExpectTrustStoreContains(FROM_HERE, target_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, newintermediate_, {newroot_});
ExpectTrustStoreContains(FROM_HERE, oldintermediate_, {newroot_});
ExpectTrustStoreContains(FROM_HERE, newrootrollover_, {newroot_});
ExpectTrustStoreContains(FROM_HERE, oldroot_->cert(), {newroot_});
ExpectTrustStoreContains(FROM_HERE, newroot_->cert(), {newroot_});
}
// When an intermediate certificate is trusted, FindTrustAnchorsForCert should
// return that cert on any certs issued by the intermediate, but not for the
// intermediate itself (or the CAs).
TEST_F(TrustStoreNSSTest, TrustedIntermediate) {
AddCertsToNSS();
TrustCert(newintermediate_.get());
ExpectTrustStoreContains(
FROM_HERE, target_,
{TrustAnchor::CreateFromCertificateNoConstraints(newintermediate_)});
ExpectTrustStoreContains(FROM_HERE, newintermediate_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, oldintermediate_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, newrootrollover_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, oldroot_->cert(), TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, newroot_->cert(), TrustAnchors());
}
// Multiple self-signed CA certificates with the same name are trusted.
// FindTrustAnchorsForCert should return all these certs on any intermediates
// with a matching issuer, and on any matching self-signed/self-issued CA certs.
TEST_F(TrustStoreNSSTest, MultipleTrustedCAWithSameSubject) {
AddCertsToNSS();
TrustCert(oldroot_.get());
TrustCert(newroot_.get());
ExpectTrustStoreContains(FROM_HERE, target_, TrustAnchors());
ExpectTrustStoreContains(FROM_HERE, newintermediate_, {newroot_, oldroot_});
ExpectTrustStoreContains(FROM_HERE, oldintermediate_, {newroot_, oldroot_});
ExpectTrustStoreContains(FROM_HERE, oldroot_->cert(), {newroot_, oldroot_});
}
// Cancel a FindTrustAnchorsForCert request before it has returned any results.
// Callback should not be called.
TEST_F(TrustStoreNSSTest, CancelRequest) {
std::unique_ptr<TrustStore::Request> req;
TrustAnchors sync_matches;
trust_store_nss_->FindTrustAnchorsForCert(target_, base::Bind(&NotCalled),
&sync_matches, &req);
ASSERT_TRUE(req);
req.reset();
base::RunLoop().RunUntilIdle();
}
// Cancel a FindTrustAnchorsForCert request during the callback. Should not
// crash.
TEST_F(TrustStoreNSSTest, CancelRequestDuringCallback) {
AddCertsToNSS();
TrustCert(newroot_.get());
base::RunLoop run_loop;
std::unique_ptr<TrustStore::Request> req;
TrustAnchors sync_matches;
trust_store_nss_->FindTrustAnchorsForCert(
newintermediate_,
base::Bind(&TrustStoreRequestDeleter, &req, run_loop.QuitClosure()),
&sync_matches, &req);
ASSERT_TRUE(req);
run_loop.Run();
ASSERT_FALSE(req);
base::RunLoop().RunUntilIdle();
}
} // namespace
} // namespace net