| // 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 <memory> |
| |
| #include "base/strings/string_number_conversions.h" |
| #include "crypto/scoped_test_nss_db.h" |
| #include "net/cert/internal/cert_issuer_source_sync_unittest.h" |
| #include "net/cert/internal/test_helpers.h" |
| #include "net/cert/scoped_nss_types.h" |
| #include "net/cert/x509_util_nss.h" |
| #include "starboard/types.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; |
| ReadCertChainFromFile( |
| "net/data/verify_certificate_chain_unittest/key-rollover/oldchain.pem", |
| &chain); |
| |
| ASSERT_EQ(3U, chain.size()); |
| target_ = chain[0]; |
| oldintermediate_ = chain[1]; |
| oldroot_ = chain[2]; |
| ASSERT_TRUE(target_); |
| ASSERT_TRUE(oldintermediate_); |
| ASSERT_TRUE(oldroot_); |
| |
| ReadCertChainFromFile( |
| "net/data/verify_certificate_chain_unittest/" |
| "key-rollover/longrolloverchain.pem", |
| &chain); |
| |
| ASSERT_EQ(5U, 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(trustSSL)); |
| } |
| |
| std::string GetUniqueNickname() { |
| return "trust_store_nss_unittest" + base::UintToString(nickname_counter_++); |
| } |
| |
| void AddCertToNSS(const ParsedCertificate* cert) { |
| ScopedCERTCertificate nss_cert(x509_util::CreateCERTCertificateFromBytes( |
| cert->der_cert().UnsafeData(), cert->der_cert().Length())); |
| ASSERT_TRUE(nss_cert); |
| SECStatus srv = PK11_ImportCert( |
| test_nssdb_.slot(), nss_cert.get(), CK_INVALID_HANDLE, |
| GetUniqueNickname().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()); |
| |
| // Check that the certificates can be retrieved as expected. |
| EXPECT_TRUE( |
| TrustStoreContains(target_, {newintermediate_, oldintermediate_})); |
| |
| EXPECT_TRUE(TrustStoreContains(newintermediate_, |
| {newroot_, newrootrollover_, oldroot_})); |
| EXPECT_TRUE(TrustStoreContains(oldintermediate_, |
| {newroot_, newrootrollover_, oldroot_})); |
| EXPECT_TRUE(TrustStoreContains(newrootrollover_, |
| {newroot_, newrootrollover_, oldroot_})); |
| EXPECT_TRUE( |
| TrustStoreContains(oldroot_, {newroot_, newrootrollover_, oldroot_})); |
| EXPECT_TRUE( |
| TrustStoreContains(newroot_, {newroot_, newrootrollover_, oldroot_})); |
| } |
| |
| // Trusts |cert|. Assumes the cert was already imported into NSS. |
| void TrustCert(const ParsedCertificate* cert) { |
| ChangeCertTrust(cert, CERTDB_TRUSTED_CA | CERTDB_VALID_CA); |
| } |
| |
| // Trusts |cert| as a server, but not as a CA. Assumes the cert was already |
| // imported into NSS. |
| void TrustServerCert(const ParsedCertificate* cert) { |
| ChangeCertTrust(cert, CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED); |
| } |
| |
| // Distrusts |cert|. Assumes the cert was already imported into NSS. |
| void DistrustCert(const ParsedCertificate* cert) { |
| ChangeCertTrust(cert, CERTDB_TERMINAL_RECORD); |
| } |
| |
| void ChangeCertTrust(const ParsedCertificate* cert, int flags) { |
| 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 = flags; |
| SECStatus srv = |
| CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nss_cert.get(), &trust); |
| ASSERT_EQ(SECSuccess, srv); |
| } |
| |
| protected: |
| bool TrustStoreContains(scoped_refptr<ParsedCertificate> cert, |
| ParsedCertificateList expected_matches) { |
| ParsedCertificateList matches; |
| trust_store_nss_->SyncGetIssuersOf(cert.get(), &matches); |
| |
| std::vector<std::string> name_result_matches; |
| for (const auto& it : matches) |
| name_result_matches.push_back(GetCertString(it)); |
| std::sort(name_result_matches.begin(), name_result_matches.end()); |
| |
| std::vector<std::string> name_expected_matches; |
| for (const auto& it : expected_matches) |
| name_expected_matches.push_back(GetCertString(it)); |
| std::sort(name_expected_matches.begin(), name_expected_matches.end()); |
| |
| if (name_expected_matches == name_result_matches) |
| return true; |
| |
| // Print some extra information for debugging. |
| EXPECT_EQ(name_expected_matches, name_result_matches); |
| return false; |
| } |
| |
| // Give simpler names to certificate DER (for identifying them in tests by |
| // their symbolic name). |
| std::string GetCertString( |
| const scoped_refptr<ParsedCertificate>& cert) const { |
| if (cert->der_cert() == oldroot_->der_cert()) |
| return "oldroot_"; |
| if (cert->der_cert() == newroot_->der_cert()) |
| return "newroot_"; |
| if (cert->der_cert() == target_->der_cert()) |
| return "target_"; |
| if (cert->der_cert() == oldintermediate_->der_cert()) |
| return "oldintermediate_"; |
| if (cert->der_cert() == newintermediate_->der_cert()) |
| return "newintermediate_"; |
| if (cert->der_cert() == newrootrollover_->der_cert()) |
| return "newrootrollover_"; |
| return cert->der_cert().AsString(); |
| } |
| |
| bool HasTrust(const ParsedCertificateList& certs, |
| CertificateTrustType expected_trust) { |
| bool success = true; |
| for (const scoped_refptr<ParsedCertificate>& cert : certs) { |
| CertificateTrust trust; |
| trust_store_nss_->GetTrust(cert.get(), &trust); |
| if (trust.type != expected_trust) { |
| EXPECT_EQ(expected_trust, trust.type) << GetCertString(cert); |
| success = false; |
| } |
| } |
| |
| return success; |
| } |
| |
| scoped_refptr<ParsedCertificate> oldroot_; |
| scoped_refptr<ParsedCertificate> 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) { |
| EXPECT_TRUE(TrustStoreContains(target_, ParsedCertificateList())); |
| EXPECT_TRUE(TrustStoreContains(newintermediate_, ParsedCertificateList())); |
| EXPECT_TRUE(TrustStoreContains(newroot_, ParsedCertificateList())); |
| } |
| |
| // 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(); |
| |
| // None of the certificates are trusted. |
| EXPECT_TRUE(HasTrust({oldroot_, newroot_, target_, oldintermediate_, |
| newintermediate_, newrootrollover_}, |
| CertificateTrustType::UNSPECIFIED)); |
| } |
| |
| // Trust a single self-signed CA certificate. |
| TEST_F(TrustStoreNSSTest, TrustedCA) { |
| AddCertsToNSS(); |
| TrustCert(newroot_.get()); |
| |
| // Only one of the certificates are trusted. |
| EXPECT_TRUE(HasTrust( |
| {oldroot_, target_, oldintermediate_, newintermediate_, newrootrollover_}, |
| CertificateTrustType::UNSPECIFIED)); |
| |
| EXPECT_TRUE(HasTrust({newroot_}, CertificateTrustType::TRUSTED_ANCHOR)); |
| } |
| |
| // Distrust a single self-signed CA certificate. |
| TEST_F(TrustStoreNSSTest, DistrustedCA) { |
| AddCertsToNSS(); |
| DistrustCert(newroot_.get()); |
| |
| // Only one of the certificates are trusted. |
| EXPECT_TRUE(HasTrust( |
| {oldroot_, target_, oldintermediate_, newintermediate_, newrootrollover_}, |
| CertificateTrustType::UNSPECIFIED)); |
| |
| EXPECT_TRUE(HasTrust({newroot_}, CertificateTrustType::DISTRUSTED)); |
| } |
| |
| // Trust a single intermediate certificate. |
| TEST_F(TrustStoreNSSTest, TrustedIntermediate) { |
| AddCertsToNSS(); |
| TrustCert(newintermediate_.get()); |
| |
| EXPECT_TRUE(HasTrust( |
| {oldroot_, newroot_, target_, oldintermediate_, newrootrollover_}, |
| CertificateTrustType::UNSPECIFIED)); |
| EXPECT_TRUE( |
| HasTrust({newintermediate_}, CertificateTrustType::TRUSTED_ANCHOR)); |
| } |
| |
| // Distrust a single intermediate certificate. |
| TEST_F(TrustStoreNSSTest, DistrustedIntermediate) { |
| AddCertsToNSS(); |
| DistrustCert(newintermediate_.get()); |
| |
| EXPECT_TRUE(HasTrust( |
| {oldroot_, newroot_, target_, oldintermediate_, newrootrollover_}, |
| CertificateTrustType::UNSPECIFIED)); |
| EXPECT_TRUE(HasTrust({newintermediate_}, CertificateTrustType::DISTRUSTED)); |
| } |
| |
| // Trust a single server certificate. |
| TEST_F(TrustStoreNSSTest, TrustedServer) { |
| AddCertsToNSS(); |
| TrustServerCert(target_.get()); |
| |
| // Server-trusted certificates are handled as UNSPECIFIED since we don't |
| // support the notion of explictly trusted server certs. See |
| // https://crbug.com/814994. |
| EXPECT_TRUE(HasTrust({oldroot_, newroot_, target_, oldintermediate_, |
| newintermediate_, newrootrollover_}, |
| CertificateTrustType::UNSPECIFIED)); |
| } |
| |
| // Trust multiple self-signed CA certificates with the same name. |
| TEST_F(TrustStoreNSSTest, MultipleTrustedCAWithSameSubject) { |
| AddCertsToNSS(); |
| TrustCert(oldroot_.get()); |
| TrustCert(newroot_.get()); |
| |
| EXPECT_TRUE( |
| HasTrust({target_, oldintermediate_, newintermediate_, newrootrollover_}, |
| CertificateTrustType::UNSPECIFIED)); |
| EXPECT_TRUE( |
| HasTrust({oldroot_, newroot_}, CertificateTrustType::TRUSTED_ANCHOR)); |
| } |
| |
| // Different trust settings for multiple self-signed CA certificates with the |
| // same name. |
| TEST_F(TrustStoreNSSTest, DifferingTrustCAWithSameSubject) { |
| AddCertsToNSS(); |
| DistrustCert(oldroot_.get()); |
| TrustCert(newroot_.get()); |
| |
| EXPECT_TRUE( |
| HasTrust({target_, oldintermediate_, newintermediate_, newrootrollover_}, |
| CertificateTrustType::UNSPECIFIED)); |
| EXPECT_TRUE(HasTrust({oldroot_}, CertificateTrustType::DISTRUSTED)); |
| EXPECT_TRUE(HasTrust({newroot_}, CertificateTrustType::TRUSTED_ANCHOR)); |
| } |
| |
| class TrustStoreNSSTestDelegate { |
| public: |
| TrustStoreNSSTestDelegate() : trust_store_nss_(trustSSL) {} |
| |
| void AddCert(scoped_refptr<ParsedCertificate> cert) { |
| ASSERT_TRUE(test_nssdb_.is_open()); |
| ScopedCERTCertificate nss_cert(x509_util::CreateCERTCertificateFromBytes( |
| cert->der_cert().UnsafeData(), cert->der_cert().Length())); |
| ASSERT_TRUE(nss_cert); |
| SECStatus srv = PK11_ImportCert( |
| test_nssdb_.slot(), nss_cert.get(), CK_INVALID_HANDLE, |
| GetUniqueNickname().c_str(), PR_FALSE /* includeTrust (unused) */); |
| ASSERT_EQ(SECSuccess, srv); |
| } |
| |
| CertIssuerSource& source() { return trust_store_nss_; } |
| |
| protected: |
| std::string GetUniqueNickname() { |
| return "cert_issuer_source_nss_unittest" + |
| base::UintToString(nickname_counter_++); |
| } |
| |
| crypto::ScopedTestNSSDB test_nssdb_; |
| TrustStoreNSS trust_store_nss_; |
| unsigned int nickname_counter_ = 0; |
| }; |
| |
| INSTANTIATE_TYPED_TEST_CASE_P(TrustStoreNSSTest2, |
| CertIssuerSourceSyncTest, |
| TrustStoreNSSTestDelegate); |
| |
| // NSS doesn't normalize UTF8String values, so use the not-normalized version of |
| // those tests. |
| INSTANTIATE_TYPED_TEST_CASE_P(TrustStoreNSSNotNormalizedTest, |
| CertIssuerSourceSyncNotNormalizedTest, |
| TrustStoreNSSTestDelegate); |
| |
| } // namespace |
| |
| } // namespace net |