| /* 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; |
| } |