Bug 1434936 - Add method nsNSSCertList::GetRootCertificate r?keeler r?fkiefer
This adds another utility method to nsNSSCertList to perform CERT_LIST_TAIL on
the underlying certificate list and return the last entry -- e.g., the root.
This is a convenience method to let other parts of the certificate verifier
continue to work with the higher-level nsNSSCertificate objects instead of
having to convert them.
MozReview-Commit-ID: EEi9L5Iepc6
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -1270,16 +1270,38 @@ nsNSSCertList::SegmentCertificateChain(/
if (!aRoot || !aEndEntity) {
// No self-sigend (or empty) chains allowed
return NS_ERROR_INVALID_ARG;
}
return NS_OK;
}
+nsresult
+nsNSSCertList::GetRootCertificate(/* out */ nsCOMPtr<nsIX509Cert>& aRoot)
+{
+ if (aRoot) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ CERTCertListNode* rootNode = CERT_LIST_TAIL(mCertList);
+ if (!rootNode) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ if (CERT_LIST_END(rootNode, mCertList)) {
+ // Empty list, leave aRoot empty
+ return NS_OK;
+ }
+ // Duplicates the certificate
+ aRoot = nsNSSCertificate::Create(rootNode->cert);
+ if (!aRoot) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+}
+
NS_IMPL_ISUPPORTS(nsNSSCertListEnumerator, nsISimpleEnumerator)
nsNSSCertListEnumerator::nsNSSCertListEnumerator(
const UniqueCERTCertList& certList)
{
MOZ_ASSERT(certList);
mCertList = nsNSSCertList::DupCertList(certList);
}
--- a/security/manager/ssl/nsNSSCertificate.h
+++ b/security/manager/ssl/nsNSSCertificate.h
@@ -101,16 +101,21 @@ public:
// doubt.
// Will return error if used on self-signed or empty chains.
// This method requires that all arguments be empty, notably the list
// `aIntermediates` must be empty.
nsresult SegmentCertificateChain(/* out */ nsCOMPtr<nsIX509Cert>& aRoot,
/* out */ nsCOMPtr<nsIX509CertList>& aIntermediates,
/* out */ nsCOMPtr<nsIX509Cert>& aEndEntity);
+ // Obtain the root certificate of a certificate chain. This method does so
+ // blindly, as SegmentCertificateChain; the same restrictions apply. On an
+ // empty list, leaves aRoot empty and returns OK.
+ nsresult GetRootCertificate(/* out */ nsCOMPtr<nsIX509Cert>& aRoot);
+
private:
virtual ~nsNSSCertList() {}
mozilla::UniqueCERTCertList mCertList;
nsNSSCertList(const nsNSSCertList&) = delete;
void operator=(const nsNSSCertList&) = delete;
};
--- a/security/manager/ssl/tests/gtest/CertListTest.cpp
+++ b/security/manager/ssl/tests/gtest/CertListTest.cpp
@@ -369,17 +369,16 @@ TEST_F(psm_CertList, TestForEachStopEarl
counter++;
aContinue = false;
return NS_OK;
});
ASSERT_EQ(counter, 1) << "There should have been only the one call";
ASSERT_EQ(rv, NS_OK) << "Should complete OK.";
}
-
TEST_F(psm_CertList, TestForEachStopOnError)
{
RefPtr<nsNSSCertList> certList = new nsNSSCertList();
// Add two certificates
nsresult rv = AddCertFromStringToList(kCaSecondIntermediatePem, certList);
ASSERT_EQ(rv, NS_OK) << "Should have loaded OK";
rv = AddCertFromStringToList(kEePem, certList);
@@ -388,9 +387,79 @@ TEST_F(psm_CertList, TestForEachStopOnEr
int counter = 0;
rv = certList->ForEachCertificateInChain(
[&counter] (nsCOMPtr<nsIX509Cert> aCert, bool aHasMore, bool& aContinue) {
counter++;
return NS_ERROR_FAILURE;
});
ASSERT_EQ(counter, 1) << "There should have been only the one call";
ASSERT_EQ(rv, NS_ERROR_FAILURE) << "Should propagate the error.";
-}
\ No newline at end of file
+}
+
+TEST_F(psm_CertList, TestGetRootCertificateChainTwo)
+{
+ RefPtr<nsNSSCertList> certList = new nsNSSCertList();
+
+ nsresult rv = AddCertFromStringToList(kCaIntermediatePem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+ rv = AddCertFromStringToList(kCaPem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+
+ nsCOMPtr<nsIX509Cert> rootCert;
+ rv = certList->GetRootCertificate(rootCert);
+ EXPECT_EQ(NS_OK, rv) << "Should have fetched the root OK";
+ ASSERT_TRUE(rootCert) << "Root cert should be filled in";
+
+ bool selfSigned;
+ EXPECT_TRUE(NS_SUCCEEDED(rootCert->GetIsSelfSigned(&selfSigned))) << "Getters should work.";
+ EXPECT_TRUE(selfSigned) << "Roots are self signed";
+
+ nsAutoString rootCn;
+ EXPECT_TRUE(NS_SUCCEEDED(rootCert->GetCommonName(rootCn))) << "Getters should work.";
+ EXPECT_TRUE(rootCn.EqualsLiteral("ca")) << "Root CN should match";
+
+ // Re-fetch and ensure we get the same certificate.
+ nsCOMPtr<nsIX509Cert> rootCertRepeat;
+ rv = certList->GetRootCertificate(rootCertRepeat);
+ EXPECT_EQ(NS_OK, rv) << "Should have fetched the root OK the second time";
+ ASSERT_TRUE(rootCertRepeat) << "Root cert should still be filled in";
+
+ nsAutoString rootRepeatCn;
+ EXPECT_TRUE(NS_SUCCEEDED(rootCertRepeat->GetCommonName(rootRepeatCn))) << "Getters should work.";
+ EXPECT_TRUE(rootRepeatCn.EqualsLiteral("ca")) << "Root CN should still match";
+}
+
+TEST_F(psm_CertList, TestGetRootCertificateChainFour)
+{
+ RefPtr<nsNSSCertList> certList = new nsNSSCertList();
+
+ nsresult rv = AddCertFromStringToList(kEePem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+ rv = AddCertFromStringToList(kCaSecondIntermediatePem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+ rv = AddCertFromStringToList(kCaIntermediatePem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+ rv = AddCertFromStringToList(kCaPem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+
+ nsCOMPtr<nsIX509Cert> rootCert;
+ rv = certList->GetRootCertificate(rootCert);
+ EXPECT_EQ(NS_OK, rv) << "Should have again fetched the root OK";
+ ASSERT_TRUE(rootCert) << "Root cert should be filled in";
+
+ bool selfSigned;
+ EXPECT_TRUE(NS_SUCCEEDED(rootCert->GetIsSelfSigned(&selfSigned))) << "Getters should work.";
+ EXPECT_TRUE(selfSigned) << "Roots are self signed";
+
+ nsAutoString rootCn;
+ EXPECT_TRUE(NS_SUCCEEDED(rootCert->GetCommonName(rootCn))) << "Getters should work.";
+ EXPECT_TRUE(rootCn.EqualsLiteral("ca")) << "Root CN should match";
+}
+
+TEST_F(psm_CertList, TestGetRootCertificateChainEmpty)
+{
+ RefPtr<nsNSSCertList> certList = new nsNSSCertList();
+
+ nsCOMPtr<nsIX509Cert> rootCert;
+ nsresult rv = certList->GetRootCertificate(rootCert);
+ EXPECT_EQ(NS_OK, rv) << "Should have again fetched the root OK";
+ EXPECT_FALSE(rootCert) << "Root cert should be empty";
+}