summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp182
1 files changed, 182 insertions, 0 deletions
diff --git a/security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp b/security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp
new file mode 100644
index 0000000000..5d79aeb232
--- /dev/null
+++ b/security/nss/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);
+}