summaryrefslogtreecommitdiff
path: root/gtests/mozpkix_gtest
diff options
context:
space:
mode:
authorDana Keeler <dkeeler@mozilla.com>2021-05-18 17:30:21 +0000
committerDana Keeler <dkeeler@mozilla.com>2021-05-18 17:30:21 +0000
commit1afcaab5dbb8816221287d880fe4ec85e8b4b24e (patch)
treeb956a3afc900a2b3927797aa4c9ecafe2a2aa229 /gtests/mozpkix_gtest
parent1eaaf9b21673a9e62355a8efe452e50f35dd712d (diff)
downloadnss-hg-1afcaab5dbb8816221287d880fe4ec85e8b4b24e.tar.gz
Bug 1709291 - add VerifyCodeSigningCertificateChain r=bbeurdouche
Differential Revision: https://phabricator.services.mozilla.com/D114660
Diffstat (limited to 'gtests/mozpkix_gtest')
-rw-r--r--gtests/mozpkix_gtest/mozpkix_gtest.gyp1
-rw-r--r--gtests/mozpkix_gtest/pkixc_tests.cpp182
-rw-r--r--gtests/mozpkix_gtest/pkixcheck_CheckExtendedKeyUsage_tests.cpp23
-rw-r--r--gtests/mozpkix_gtest/pkixgtest.h27
4 files changed, 207 insertions, 26 deletions
diff --git a/gtests/mozpkix_gtest/mozpkix_gtest.gyp b/gtests/mozpkix_gtest/mozpkix_gtest.gyp
index 1623d76bb..80c348888 100644
--- a/gtests/mozpkix_gtest/mozpkix_gtest.gyp
+++ b/gtests/mozpkix_gtest/mozpkix_gtest.gyp
@@ -13,6 +13,7 @@
'sources': [
'<(DEPTH)/gtests/common/gtests.cc',
'pkixbuild_tests.cpp',
+ 'pkixc_tests.cpp',
'pkixcert_extension_tests.cpp',
'pkixcert_signature_algorithm_tests.cpp',
'pkixcheck_CheckExtendedKeyUsage_tests.cpp',
diff --git a/gtests/mozpkix_gtest/pkixc_tests.cpp b/gtests/mozpkix_gtest/pkixc_tests.cpp
new file mode 100644
index 000000000..5d79aeb23
--- /dev/null
+++ b/gtests/mozpkix_gtest/pkixc_tests.cpp
@@ -0,0 +1,182 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "pkixgtest.h"
+
+#include "mozpkix/pkixc.h"
+#include "mozpkix/pkixder.h"
+#include "mozpkix/pkixnss.h"
+#include "secerr.h"
+#include "sslerr.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+static ByteString CreateCert(
+ const char* issuerCN, const char* subjectCN, EndEntityOrCA endEntityOrCA,
+ /*optional*/ const ByteString* subjectAlternativeNameExtension = nullptr,
+ /*optional*/ const ByteString* extendedKeyUsageExtension = nullptr) {
+ EXPECT_TRUE(issuerCN);
+ EXPECT_TRUE(subjectCN);
+ static long serialNumberValue = 0;
+ ++serialNumberValue;
+ ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
+ EXPECT_FALSE(ENCODING_FAILED(serialNumber));
+
+ ByteString issuerDER(CNToDERName(issuerCN));
+ ByteString subjectDER(CNToDERName(subjectCN));
+
+ std::time_t notBefore = 1620000000;
+ std::time_t notAfter = 1630000000;
+
+ std::vector<ByteString> extensions;
+ if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
+ ByteString basicConstraints =
+ CreateEncodedBasicConstraints(true, nullptr, Critical::Yes);
+ EXPECT_FALSE(ENCODING_FAILED(basicConstraints));
+ extensions.push_back(basicConstraints);
+ }
+ if (subjectAlternativeNameExtension) {
+ extensions.push_back(*subjectAlternativeNameExtension);
+ }
+ if (extendedKeyUsageExtension) {
+ extensions.push_back(*extendedKeyUsageExtension);
+ }
+ extensions.push_back(ByteString()); // marks the end of the list
+
+ ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
+ ByteString certDER(CreateEncodedCertificate(
+ v3, sha256WithRSAEncryption(), serialNumber, issuerDER, notBefore,
+ notAfter, subjectDER, *reusedKey, extensions.data(), *reusedKey,
+ sha256WithRSAEncryption()));
+ EXPECT_FALSE(ENCODING_FAILED(certDER));
+
+ return certDER;
+}
+
+class pkixc_tests : public ::testing::Test {};
+
+TEST_F(pkixc_tests, Valid_VerifyCodeSigningCertificateChain) {
+ ByteString root(CreateCert("CA", "CA", EndEntityOrCA::MustBeCA));
+ ByteString intermediate(
+ CreateCert("CA", "intermediate", EndEntityOrCA::MustBeCA));
+ ByteString subjectAltNameExtension =
+ CreateEncodedSubjectAltName(DNSName("example.com"));
+ ByteString endEntity(CreateCert("intermediate", "end-entity",
+ EndEntityOrCA::MustBeEndEntity,
+ &subjectAltNameExtension));
+ const uint8_t* certificates[] = {endEntity.data(), intermediate.data(),
+ root.data()};
+ const uint16_t certificateLengths[] = {
+ static_cast<uint16_t>(endEntity.length()),
+ static_cast<uint16_t>(intermediate.length()),
+ static_cast<uint16_t>(root.length())};
+ const size_t numCertificates = 3;
+ const uint64_t secondsSinceEpoch = 1625000000;
+ uint8_t rootSHA256Digest[32] = {0};
+ Input rootInput;
+ Result rv = rootInput.Init(root.data(), root.length());
+ ASSERT_EQ(rv, Success);
+ rv = DigestBufNSS(rootInput, DigestAlgorithm::sha256, rootSHA256Digest,
+ sizeof(rootSHA256Digest));
+ ASSERT_EQ(rv, Success);
+ const uint8_t hostname[] = {"example.com"};
+ size_t hostnameLength = strlen("example.com");
+ PRErrorCode error = 0;
+ ASSERT_TRUE(VerifyCodeSigningCertificateChain(
+ &certificates[0], &certificateLengths[0], numCertificates,
+ secondsSinceEpoch, &rootSHA256Digest[0], &hostname[0], hostnameLength,
+ &error));
+
+ // If the extended key usage extension is present, it must have the code
+ // signing usage.
+ ByteString extendedKeyUsageExtension(
+ CreateEKUExtension(BytesToByteString(tlv_id_kp_codeSigning)));
+ ByteString endEntityWithEKU(
+ CreateCert("intermediate", "end-entity", EndEntityOrCA::MustBeEndEntity,
+ &subjectAltNameExtension, &extendedKeyUsageExtension));
+ const uint8_t* certificatesWithEKU[] = {endEntityWithEKU.data(),
+ intermediate.data(), root.data()};
+ const uint16_t certificateLengthsWithEKU[] = {
+ static_cast<uint16_t>(endEntityWithEKU.length()),
+ static_cast<uint16_t>(intermediate.length()),
+ static_cast<uint16_t>(root.length())};
+ ASSERT_TRUE(VerifyCodeSigningCertificateChain(
+ &certificatesWithEKU[0], &certificateLengthsWithEKU[0], numCertificates,
+ secondsSinceEpoch, &rootSHA256Digest[0], &hostname[0], hostnameLength,
+ &error));
+}
+
+TEST_F(pkixc_tests, Invalid_VerifyCodeSigningCertificateChain) {
+ ByteString root(CreateCert("CA", "CA", EndEntityOrCA::MustBeCA));
+ ByteString subjectAltNameExtension =
+ CreateEncodedSubjectAltName(DNSName("example.com"));
+ ByteString endEntity(CreateCert("CA", "end-entity",
+ EndEntityOrCA::MustBeEndEntity,
+ &subjectAltNameExtension));
+ const uint8_t* certificates[] = {endEntity.data(), root.data()};
+ const uint16_t certificateLengths[] = {
+ static_cast<uint16_t>(endEntity.length()),
+ static_cast<uint16_t>(root.length())};
+ const size_t numCertificates = 2;
+ const uint64_t secondsSinceEpoch = 1625000000;
+ uint8_t rootSHA256Digest[32] = {0};
+ Input rootInput;
+ Result rv = rootInput.Init(root.data(), root.length());
+ ASSERT_EQ(rv, Success);
+ rv = DigestBufNSS(rootInput, DigestAlgorithm::sha256, rootSHA256Digest,
+ sizeof(rootSHA256Digest));
+ ASSERT_EQ(rv, Success);
+ const uint8_t hostname[] = {"example.com"};
+ size_t hostnameLength = strlen("example.com");
+ PRErrorCode error = 0;
+ // Consistency check first to ensure these tests are meaningful.
+ ASSERT_TRUE(VerifyCodeSigningCertificateChain(
+ &certificates[0], &certificateLengths[0], numCertificates,
+ secondsSinceEpoch, &rootSHA256Digest[0], &hostname[0], hostnameLength,
+ &error));
+ ASSERT_EQ(error, 0);
+
+ // Test with "now" after the certificates have expired.
+ ASSERT_FALSE(VerifyCodeSigningCertificateChain(
+ &certificates[0], &certificateLengths[0], numCertificates,
+ secondsSinceEpoch + 10000000, &rootSHA256Digest[0], &hostname[0],
+ hostnameLength, &error));
+ ASSERT_EQ(error, SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE);
+
+ // Test with a different root digest.
+ uint8_t wrongRootSHA256Digest[32] = {1};
+ ASSERT_FALSE(VerifyCodeSigningCertificateChain(
+ &certificates[0], &certificateLengths[0], numCertificates,
+ secondsSinceEpoch, &wrongRootSHA256Digest[0], &hostname[0],
+ hostnameLength, &error));
+ ASSERT_EQ(error, SEC_ERROR_UNKNOWN_ISSUER);
+
+ // Test with a different host name.
+ const uint8_t wrongHostname[] = "example.org";
+ size_t wrongHostnameLength = strlen("example.org");
+ ASSERT_FALSE(VerifyCodeSigningCertificateChain(
+ &certificates[0], &certificateLengths[0], numCertificates,
+ secondsSinceEpoch, &rootSHA256Digest[0], &wrongHostname[0],
+ wrongHostnameLength, &error));
+ ASSERT_EQ(error, SSL_ERROR_BAD_CERT_DOMAIN);
+
+ // Test with a certificate with an extended key usage that doesn't include
+ // code signing.
+ ByteString extendedKeyUsageExtension(
+ CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)));
+ ByteString endEntityWithEKU(
+ CreateCert("CA", "end-entity", EndEntityOrCA::MustBeEndEntity,
+ &subjectAltNameExtension, &extendedKeyUsageExtension));
+ const uint8_t* certificatesWithEKU[] = {endEntityWithEKU.data(), root.data()};
+ const uint16_t certificateLengthsWithEKU[] = {
+ static_cast<uint16_t>(endEntityWithEKU.length()),
+ static_cast<uint16_t>(root.length())};
+ ASSERT_FALSE(VerifyCodeSigningCertificateChain(
+ &certificatesWithEKU[0], &certificateLengthsWithEKU[0], numCertificates,
+ secondsSinceEpoch, &rootSHA256Digest[0], &hostname[0], hostnameLength,
+ &error));
+ ASSERT_EQ(error, SEC_ERROR_INADEQUATE_CERT_TYPE);
+}
diff --git a/gtests/mozpkix_gtest/pkixcheck_CheckExtendedKeyUsage_tests.cpp b/gtests/mozpkix_gtest/pkixcheck_CheckExtendedKeyUsage_tests.cpp
index 3fd169517..549d030af 100644
--- a/gtests/mozpkix_gtest/pkixcheck_CheckExtendedKeyUsage_tests.cpp
+++ b/gtests/mozpkix_gtest/pkixcheck_CheckExtendedKeyUsage_tests.cpp
@@ -49,15 +49,7 @@ protected:
// tlv_id_kp_OCSPSigning and tlv_id_kp_serverAuth are defined in pkixtestutil.h
-// python DottedOIDToCode.py --tlv id-kp-clientAuth 1.3.6.1.5.5.7.3.2
-static const uint8_t tlv_id_kp_clientAuth[] = {
- 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02
-};
-
-// python DottedOIDToCode.py --tlv id-kp-codeSigning 1.3.6.1.5.5.7.3.3
-static const uint8_t tlv_id_kp_codeSigning[] = {
- 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03
-};
+// tlv_id_kp_clientAuth and tlv_id_kp_codeSigning are defined in pkixgtest.h
// python DottedOIDToCode.py --tlv id_kp_emailProtection 1.3.6.1.5.5.7.3.4
static const uint8_t tlv_id_kp_emailProtection[] = {
@@ -595,19 +587,6 @@ TEST_P(CheckExtendedKeyUsageChainTest, EKUChainTestcase)
nullptr));
}
-// python DottedOIDToCode.py --tlv id-ce-extKeyUsage 2.5.29.37
-static const uint8_t tlv_id_ce_extKeyUsage[] = {
- 0x06, 0x03, 0x55, 0x1d, 0x25
-};
-
-static inline ByteString
-CreateEKUExtension(ByteString ekuOIDs)
-{
- return TLV(der::SEQUENCE,
- BytesToByteString(tlv_id_ce_extKeyUsage) +
- TLV(der::OCTET_STRING, TLV(der::SEQUENCE, ekuOIDs)));
-}
-
static const EKUChainTestcase EKU_CHAIN_TESTCASES[] =
{
{
diff --git a/gtests/mozpkix_gtest/pkixgtest.h b/gtests/mozpkix_gtest/pkixgtest.h
index 719b87d54..777f123ca 100644
--- a/gtests/mozpkix_gtest/pkixgtest.h
+++ b/gtests/mozpkix_gtest/pkixgtest.h
@@ -57,6 +57,7 @@
#endif
#include "mozpkix/pkix.h"
+#include "mozpkix/pkixder.h"
#include "mozpkix/test/pkixtestutil.h"
// PrintTo must be in the same namespace as the type we're overloading it for.
@@ -71,8 +72,8 @@ inline void PrintTo(const Result& result, ::std::ostream* os) {
*os << "mozilla::pkix::Result(" << static_cast<unsigned int>(result) << ")";
}
}
-}
-} // namespace mozilla::pkix
+} // namespace pkix
+} // namespace mozilla
namespace mozilla {
namespace pkix {
@@ -223,8 +224,26 @@ class DefaultNameMatchingPolicy : public NameMatchingPolicy {
return Success;
}
};
+
+// python DottedOIDToCode.py --tlv id-kp-clientAuth 1.3.6.1.5.5.7.3.2
+const uint8_t tlv_id_kp_clientAuth[] = {0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x02};
+
+// python DottedOIDToCode.py --tlv id-kp-codeSigning 1.3.6.1.5.5.7.3.3
+const uint8_t tlv_id_kp_codeSigning[] = {0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x03};
+
+// python DottedOIDToCode.py --tlv id-ce-extKeyUsage 2.5.29.37
+const uint8_t tlv_id_ce_extKeyUsage[] = {0x06, 0x03, 0x55, 0x1d, 0x25};
+
+inline ByteString CreateEKUExtension(ByteString ekuOIDs) {
+ return TLV(der::SEQUENCE,
+ BytesToByteString(tlv_id_ce_extKeyUsage) +
+ TLV(der::OCTET_STRING, TLV(der::SEQUENCE, ekuOIDs)));
}
-}
-} // namespace mozilla::pkix::test
+
+} // namespace test
+} // namespace pkix
+} // namespace mozilla
#endif // mozilla_pkix_pkixgtest_h