diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp')
-rw-r--r-- | security/nss/gtests/mozpkix_gtest/pkixc_tests.cpp | 182 |
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); +} |