From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp | 182 ++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp (limited to 'security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp') 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 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(endEntity.length()), + static_cast(intermediate.length()), + static_cast(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(endEntityWithEKU.length()), + static_cast(intermediate.length()), + static_cast(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(endEntity.length()), + static_cast(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(endEntityWithEKU.length()), + static_cast(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); +} -- cgit v1.2.3