diff options
author | Dana Keeler <dkeeler@mozilla.com> | 2021-05-18 17:30:21 +0000 |
---|---|---|
committer | Dana Keeler <dkeeler@mozilla.com> | 2021-05-18 17:30:21 +0000 |
commit | 1afcaab5dbb8816221287d880fe4ec85e8b4b24e (patch) | |
tree | b956a3afc900a2b3927797aa4c9ecafe2a2aa229 | |
parent | 1eaaf9b21673a9e62355a8efe452e50f35dd712d (diff) | |
download | nss-hg-1afcaab5dbb8816221287d880fe4ec85e8b4b24e.tar.gz |
Bug 1709291 - add VerifyCodeSigningCertificateChain r=bbeurdouche
Differential Revision: https://phabricator.services.mozilla.com/D114660
-rw-r--r-- | gtests/mozpkix_gtest/mozpkix_gtest.gyp | 1 | ||||
-rw-r--r-- | gtests/mozpkix_gtest/pkixc_tests.cpp | 182 | ||||
-rw-r--r-- | gtests/mozpkix_gtest/pkixcheck_CheckExtendedKeyUsage_tests.cpp | 23 | ||||
-rw-r--r-- | gtests/mozpkix_gtest/pkixgtest.h | 27 | ||||
-rw-r--r-- | lib/mozpkix/exports.gyp | 3 | ||||
-rw-r--r-- | lib/mozpkix/include/pkix/pkixc.h | 47 | ||||
-rw-r--r-- | lib/mozpkix/lib/pkixc.cpp | 218 | ||||
-rw-r--r-- | lib/mozpkix/mozpkix.gyp | 1 |
8 files changed, 475 insertions, 27 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 diff --git a/lib/mozpkix/exports.gyp b/lib/mozpkix/exports.gyp index 248efc910..d592bb70f 100644 --- a/lib/mozpkix/exports.gyp +++ b/lib/mozpkix/exports.gyp @@ -17,6 +17,7 @@ 'include/pkix/Time.h', 'include/pkix/Result.h', 'include/pkix/pkix.h', + 'include/pkix/pkixc.h', 'include/pkix/pkixnss.h', 'include/pkix/pkixtypes.h', 'include/pkix/pkixutil.h', @@ -44,4 +45,4 @@ 'variables': { 'module': 'nss' } -}
\ No newline at end of file +} diff --git a/lib/mozpkix/include/pkix/pkixc.h b/lib/mozpkix/include/pkix/pkixc.h new file mode 100644 index 000000000..a1cc7bf2e --- /dev/null +++ b/lib/mozpkix/include/pkix/pkixc.h @@ -0,0 +1,47 @@ +/* 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/. + */ + +#ifndef mozilla_pkix_pkixc_h +#define mozilla_pkix_pkixc_h + +#include "prerror.h" +#include "stdint.h" + +// VerifyCertificateChain will attempt to build a verified certificate chain +// starting from the 0th certificate in the given array to the indicated trust +// anchor. It returns true on success and false otherwise. No particular key +// usage is required, and no particular policy is required. The code signing +// extended key usage is required. No revocation checking is performed. RSA +// keys must be at least 2048 bits long, and EC keys must be from one of the +// curves secp256r1, secp384r1, or secp521r1. Only SHA256, SHA384, and SHA512 +// are acceptable digest algorithms. When doing name checking, the subject +// common name field is ignored. +// certificate is an array of pointers to certificates. +// certificateLengths is an array of the lengths of each certificate. +// numCertificates indicates how many certificates are in certificates. +// secondsSinceEpoch indicates the time at which the certificate chain must be +// valid, in seconds since the epoch. +// rootSHA256Hash identifies a trust anchor by the SHA256 hash of its contents. +// It must be an array of 32 bytes. +// hostname is a doman name for which the end-entity certificate must be valid. +// error will be set if and only if the return value is false. Its value may +// indicate why verification failed. + +#ifdef __cplusplus +extern "C" { +#endif +bool VerifyCodeSigningCertificateChain(const uint8_t** certificates, + const uint16_t* certificateLengths, + size_t numCertificates, + uint64_t secondsSinceEpoch, + const uint8_t* rootSHA256Hash, + const uint8_t* hostname, + size_t hostnameLength, + /* out */ PRErrorCode* error); +#ifdef __cplusplus +} +#endif + +#endif // mozilla_pkix_pkixc_h diff --git a/lib/mozpkix/lib/pkixc.cpp b/lib/mozpkix/lib/pkixc.cpp new file mode 100644 index 000000000..ec8cc120a --- /dev/null +++ b/lib/mozpkix/lib/pkixc.cpp @@ -0,0 +1,218 @@ +/* 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 "mozpkix/pkixc.h" + +#include "mozpkix/pkix.h" +#include "mozpkix/pkixnss.h" +#include "mozpkix/pkixtypes.h" +#include "secerr.h" + +using namespace mozilla::pkix; + +const size_t SHA256_DIGEST_LENGTH = 256 / 8; + +class CodeSigningTrustDomain final : public TrustDomain { + public: + explicit CodeSigningTrustDomain(const uint8_t** certificates, + const uint16_t* certificateLengths, + size_t numCertificates, + const uint8_t* rootSHA256Digest) + : mCertificates(certificates), + mCertificateLengths(certificateLengths), + mNumCertificates(numCertificates), + mRootSHA256Digest(rootSHA256Digest) {} + + virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, + const CertPolicyId& policy, + Input candidateCertDER, + /*out*/ TrustLevel& trustLevel) override { + uint8_t digestBuf[SHA256_DIGEST_LENGTH] = {0}; + Result rv = DigestBufNSS(candidateCertDER, DigestAlgorithm::sha256, + digestBuf, SHA256_DIGEST_LENGTH); + if (rv != Success) { + return rv; + } + Input candidateDigestInput; + rv = candidateDigestInput.Init(digestBuf, SHA256_DIGEST_LENGTH); + if (rv != Success) { + return rv; + } + Input rootDigestInput; + rv = rootDigestInput.Init(mRootSHA256Digest, SHA256_DIGEST_LENGTH); + if (rv != Success) { + return rv; + } + if (InputsAreEqual(candidateDigestInput, rootDigestInput)) { + trustLevel = TrustLevel::TrustAnchor; + } else { + trustLevel = TrustLevel::InheritsTrust; + } + return Success; + } + + virtual Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, + Time time) override { + for (size_t i = 0; i < mNumCertificates; i++) { + Input certInput; + Result rv = certInput.Init(mCertificates[i], mCertificateLengths[i]); + if (rv != Success) { + return rv; + } + bool keepGoing; + rv = checker.Check(certInput, nullptr /*additionalNameConstraints*/, + keepGoing); + if (rv != Success) { + return rv; + } + if (!keepGoing) { + break; + } + } + + return Success; + } + + virtual Result CheckRevocation( + EndEntityOrCA endEntityOrCA, const CertID& certID, Time time, + Duration validityDuration, + /*optional*/ const Input* stapledOCSPresponse, + /*optional*/ const Input* aiaExtension, + /*optional*/ const Input* sctExtension) override { + return Success; + } + + virtual Result IsChainValid(const DERArray& certChain, Time time, + const CertPolicyId& requiredPolicy) override { + return Success; + } + + virtual Result CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg, + EndEntityOrCA endEntityOrCA, + Time notBefore) override { + switch (digestAlg) { + case DigestAlgorithm::sha256: // fall through + case DigestAlgorithm::sha384: // fall through + case DigestAlgorithm::sha512: + return Success; + default: + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; + } + } + + virtual Result CheckRSAPublicKeyModulusSizeInBits( + EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) override { + if (modulusSizeInBits < 2048) { + return Result::ERROR_INADEQUATE_KEY_SIZE; + } + return Success; + } + + virtual Result VerifyRSAPKCS1SignedDigest( + const SignedDigest& signedDigest, Input subjectPublicKeyInfo) override { + return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); + } + + virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA, + NamedCurve curve) override { + switch (curve) { + case NamedCurve::secp256r1: // fall through + case NamedCurve::secp384r1: // fall through + case NamedCurve::secp521r1: + return Success; + } + + return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; + } + + virtual Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override { + return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); + } + + virtual Result CheckValidityIsAcceptable(Time notBefore, Time notAfter, + EndEntityOrCA endEntityOrCA, + KeyPurposeId keyPurpose) override { + return Success; + } + + virtual Result NetscapeStepUpMatchesServerAuth( + Time notBefore, /*out*/ bool& matches) override { + matches = false; + return Success; + } + + virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension, + Input extensionData) override {} + + virtual Result DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) override { + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); + } + + private: + const uint8_t** mCertificates; + const uint16_t* mCertificateLengths; + size_t mNumCertificates; + const uint8_t* mRootSHA256Digest; +}; + +class CodeSigningNameMatchingPolicy : public NameMatchingPolicy { + public: + virtual Result FallBackToCommonName( + Time notBefore, + /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override { + fallBackToCommonName = FallBackToSearchWithinSubject::No; + return Success; + } +}; + +bool VerifyCodeSigningCertificateChain( + const uint8_t** certificates, const uint16_t* certificateLengths, + size_t numCertificates, uint64_t secondsSinceEpoch, + const uint8_t* rootSHA256Digest, const uint8_t* hostname, + size_t hostnameLength, PRErrorCode* error) { + if (!error) { + return false; + } + if (!certificates || !certificateLengths || !rootSHA256Digest) { + *error = SEC_ERROR_INVALID_ARGS; + return false; + } + + CodeSigningTrustDomain trustDomain(certificates, certificateLengths, + numCertificates, rootSHA256Digest); + Input certificate; + Result rv = certificate.Init(certificates[0], certificateLengths[0]); + if (rv != Success) { + *error = MapResultToPRErrorCode(rv); + return false; + } + Time time = TimeFromEpochInSeconds(secondsSinceEpoch); + rv = BuildCertChain( + trustDomain, certificate, time, EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::id_kp_codeSigning, + CertPolicyId::anyPolicy, nullptr); + if (rv != Success) { + *error = MapResultToPRErrorCode(rv); + return false; + } + Input hostnameInput; + rv = hostnameInput.Init(hostname, hostnameLength); + if (rv != Success) { + *error = MapResultToPRErrorCode(rv); + return false; + } + CodeSigningNameMatchingPolicy nameMatchingPolicy; + rv = CheckCertHostname(certificate, hostnameInput, nameMatchingPolicy); + if (rv != Success) { + *error = MapResultToPRErrorCode(rv); + return false; + } + return true; +} diff --git a/lib/mozpkix/mozpkix.gyp b/lib/mozpkix/mozpkix.gyp index 1c552ba5f..abc3d149f 100644 --- a/lib/mozpkix/mozpkix.gyp +++ b/lib/mozpkix/mozpkix.gyp @@ -12,6 +12,7 @@ 'standalone_static_library': 1, 'sources': [ 'lib/pkixbuild.cpp', + 'lib/pkixc.cpp', 'lib/pkixcert.cpp', 'lib/pkixcheck.cpp', 'lib/pkixder.cpp', |