/* 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 VerifyRSAPKCS1SignedData( Input data, DigestAlgorithm digestAlgorithm, Input signature, Input subjectPublicKeyInfo) override { return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature, subjectPublicKeyInfo, nullptr); } virtual Result VerifyRSAPSSSignedData( Input data, DigestAlgorithm digestAlgorithm, Input signature, Input subjectPublicKeyInfo) override { return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, 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 VerifyECDSASignedData( Input data, DigestAlgorithm digestAlgorithm, Input signature, Input subjectPublicKeyInfo) override { return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature, 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; } virtual HandleInvalidSubjectAlternativeNamesBy HandleInvalidSubjectAlternativeNames() override { return HandleInvalidSubjectAlternativeNamesBy::Halting; } }; 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; }