/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* 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 "CSTrustDomain.h" #include "mozilla/Base64.h" #include "mozilla/Preferences.h" #include "nsNSSCertificate.h" #include "nsNSSComponent.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "pkix/pkixnss.h" using namespace mozilla::pkix; namespace mozilla { namespace psm { static LazyLogModule gTrustDomainPRLog("CSTrustDomain"); #define CSTrust_LOG(args) MOZ_LOG(gTrustDomainPRLog, LogLevel::Debug, args) CSTrustDomain::CSTrustDomain(UniqueCERTCertList& certChain) : mCertChain(certChain) , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID)) { } Result CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId& policy, Input candidateCertDER, /*out*/ TrustLevel& trustLevel) { MOZ_ASSERT(policy.IsAnyPolicy()); if (!policy.IsAnyPolicy()) { return Result::FATAL_ERROR_INVALID_ARGS; } SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER); UniqueCERTCertificate candidateCert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false, true)); if (!candidateCert) { return MapPRErrorCodeToResult(PR_GetError()); } bool isCertRevoked; nsresult nsrv = mCertBlocklist->IsCertRevoked( candidateCert->derIssuer.data, candidateCert->derIssuer.len, candidateCert->serialNumber.data, candidateCert->serialNumber.len, candidateCert->derSubject.data, candidateCert->derSubject.len, candidateCert->derPublicKey.data, candidateCert->derPublicKey.len, &isCertRevoked); if (NS_FAILED(nsrv)) { return Result::FATAL_ERROR_LIBRARY_FAILURE; } if (isCertRevoked) { CSTrust_LOG(("CSTrustDomain: certificate is revoked\n")); return Result::ERROR_REVOKED_CERTIFICATE; } // Is this cert our built-in content signing root? bool isRoot = false; nsCOMPtr component(do_GetService(PSM_COMPONENT_CONTRACTID)); if (!component) { return Result::FATAL_ERROR_LIBRARY_FAILURE; } nsrv = component->IsCertContentSigningRoot(candidateCert.get(), isRoot); if (NS_FAILED(nsrv)) { return Result::FATAL_ERROR_LIBRARY_FAILURE; } if (isRoot) { CSTrust_LOG(("CSTrustDomain: certificate is a trust anchor\n")); trustLevel = TrustLevel::TrustAnchor; return Success; } CSTrust_LOG(("CSTrustDomain: certificate is *not* a trust anchor\n")); trustLevel = TrustLevel::InheritsTrust; return Success; } Result CSTrustDomain::FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time time) { // Loop over the chain, look for a matching subject for (CERTCertListNode* n = CERT_LIST_HEAD(mCertChain); !CERT_LIST_END(n, mCertChain); n = CERT_LIST_NEXT(n)) { Input certDER; Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len); if (rv != Success) { continue; // probably too big } // if the subject does not match, try the next certificate Input subjectDER; rv = subjectDER.Init(n->cert->derSubject.data, n->cert->derSubject.len); if (rv != Success) { continue; // just try the next one } if (!InputsAreEqual(subjectDER, encodedIssuerName)) { CSTrust_LOG(("CSTrustDomain: subjects don't match\n")); continue; } // If the subject does match, try the next step bool keepGoing; rv = checker.Check(certDER, nullptr/*additionalNameConstraints*/, keepGoing); if (rv != Success) { return rv; } if (!keepGoing) { CSTrust_LOG(("CSTrustDomain: don't keep going\n")); break; } } return Success; } Result CSTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID& certID, Time time, Duration validityDuration, /*optional*/ const Input* stapledOCSPresponse, /*optional*/ const Input* aiaExtension) { // We're relying solely on the CertBlocklist for revocation - and we're // performing checks on this in GetCertTrust (as per nsNSSCertDBTrustDomain) return Success; } Result CSTrustDomain::IsChainValid(const DERArray& certChain, Time time) { // Check that our chain is not empty if (certChain.GetLength() == 0) { return Result::FATAL_ERROR_LIBRARY_FAILURE; } return Success; } Result CSTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg, EndEntityOrCA endEntityOrCA, Time notBefore) { if (digestAlg == DigestAlgorithm::sha1) { return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; } return Success; } Result CSTrustDomain::CheckRSAPublicKeyModulusSizeInBits( EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) { if (modulusSizeInBits < 2048) { return Result::ERROR_INADEQUATE_KEY_SIZE; } return Success; } Result CSTrustDomain::VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, Input subjectPublicKeyInfo) { return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, nullptr); } Result CSTrustDomain::CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA, NamedCurve curve) { switch (curve) { case NamedCurve::secp256r1: // fall through case NamedCurve::secp384r1: // fall through case NamedCurve::secp521r1: return Success; } return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; } Result CSTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest, Input subjectPublicKeyInfo) { return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, nullptr); } Result CSTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA, KeyPurposeId keyPurpose) { return Success; } Result CSTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore, /*out*/ bool& matches) { matches = false; return Success; } void CSTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/, Input /*extensionData*/) { } Result CSTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg, /*out*/ uint8_t* digestBuf, size_t digestBufLen) { return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); } } } // end namespace mozilla::psm