/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This code is made available to you under your choice of the following sets *oflicensingterms:
*/ /* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis *file,Youcanobtainoneathttp://mozilla.org/MPL/2.0/.
*/ /* Copyright 2013 Mozilla Contributors * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense.
*/
if (resultWasSet) { if (result == Success) { return NotReached("RecordResult called after finding a chain",
Result::FATAL_ERROR_INVALID_STATE);
} // If every potential issuer has the same problem (e.g. expired) and/or if // there is only one bad potential issuer, then return a more specific // error. Otherwise, punt on trying to decide which error should be // returned by returning the generic Result::ERROR_UNKNOWN_ISSUER error. if (newResult != Success && newResult != result) {
newResult = Result::ERROR_UNKNOWN_ISSUER;
}
}
result = newResult;
resultWasSet = true;
keepGoing = result != Success; return Success;
}
Result
PathBuildingStep::CheckResult() const
{ if (!resultWasSet) { return Result::ERROR_UNKNOWN_ISSUER;
} return result;
}
// The code that executes in the inner loop of BuildForward
Result
PathBuildingStep::Check(Input potentialIssuerDER, /*optional*/ const Input* additionalNameConstraints, /*out*/ bool& keepGoing)
{
BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA,
&subject);
Result rv = potentialIssuer.Init(); if (rv != Success) { return RecordResult(rv, keepGoing);
}
// Simple TrustDomain::FindIssuers implementations may pass in all possible // CA certificates without any filtering. Because of this, we don't consider // a mismatched name to be an error. Instead, we just pretend that any // certificate without a matching name was never passed to us. In particular, // we treat the case where the TrustDomain only asks us to check CA // certificates with mismatched names as equivalent to the case where the // TrustDomain never called Check() at all. if (!InputsAreEqual(potentialIssuer.GetSubject(), subject.GetIssuer())) {
keepGoing = true; return Success;
}
// Loop prevention, done as recommended by RFC4158 Section 5.2 // TODO: this doesn't account for subjectAltNames! // TODO(perf): This probably can and should be optimized in some way. for (const BackCert* prev = potentialIssuer.childCert; prev;
prev = prev->childCert) { if (InputsAreEqual(potentialIssuer.GetSubjectPublicKeyInfo(),
prev->GetSubjectPublicKeyInfo()) &&
InputsAreEqual(potentialIssuer.GetSubject(), prev->GetSubject())) { // XXX: error code return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing);
}
}
if (potentialIssuer.GetNameConstraints()) {
rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(),
subject, requiredEKUIfPresent); if (rv != Success) { return RecordResult(rv, keepGoing);
}
}
if (additionalNameConstraints) {
rv = CheckNameConstraints(*additionalNameConstraints, subject,
requiredEKUIfPresent); if (rv != Success) { return RecordResult(rv, keepGoing);
}
}
// If we've ran out of budget, stop searching. if (buildForwardCallBudget == 0) {
Result savedRv = RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing);
keepGoing = false; return savedRv;
}
buildForwardCallBudget--;
// RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the // subject public key MUST NOT be used to verify signatures on certificates // or CRLs unless the corresponding keyCertSign or cRLSign bit is set."
rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign,
requiredEKUIfPresent, requiredPolicy, nullptr, subCACount,
buildForwardCallBudget); if (rv != Success) { return RecordResult(rv, keepGoing);
}
if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity) { const Input* sctExtension = subject.GetSignedCertificateTimestamps(); if (sctExtension) {
Input sctList;
rv = ExtractSignedCertificateTimestampListFromExtension(*sctExtension,
sctList); if (rv != Success) { // The problem is with this certificate, and all paths through it will // fail.
Result savedRv = RecordResult(rv, keepGoing);
keepGoing = false; return savedRv;
}
trustDomain.NoteAuxiliaryExtension(AuxiliaryExtension::EmbeddedSCTList,
sctList);
}
}
// We avoid doing revocation checking for expired certificates because OCSP // responders are allowed to forget about expired certificates, and many OCSP // responders return an error when asked for the status of an expired // certificate. if (deferredSubjectError != Result::ERROR_EXPIRED_CERTIFICATE) {
CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(),
subject.GetSerialNumber());
Time notBefore(Time::uninitialized);
Time notAfter(Time::uninitialized); // This should never fail. If we're here, we've already parsed the validity // and checked that the given time is in the certificate's validity period.
rv = ParseValidity(subject.GetValidity(), ¬Before, ¬After); if (rv != Success) { return rv;
}
Duration validityDuration(notAfter, notBefore);
rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time,
validityDuration, stapledOCSPResponse,
subject.GetAuthorityInfoAccess()); if (rv != Success) { // Since this is actually a problem with the current subject certificate // (rather than the issuer), it doesn't make sense to keep going; all // paths through this certificate will fail.
Result savedRv = RecordResult(rv, keepGoing);
keepGoing = false; return savedRv;
}
}
return RecordResult(Success, keepGoing);
}
// Recursively build the path from the given subject certificate to the root. // // Be very careful about changing the order of checks. The order is significant // because it affects which error we return when a certificate or certificate // chain has multiple problems. See the error ranking documentation in // pkix/pkix.h. static Result
BuildForward(TrustDomain& trustDomain, const BackCert& subject,
Time time,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent, const CertPolicyId& requiredPolicy, /*optional*/ const Input* stapledOCSPResponse, unsignedint subCACount, unsignedint& buildForwardCallBudget)
{
Result rv;
TrustLevel trustLevel; // If this is an end-entity and not a trust anchor, we defer reporting // any error found here until after attempting to find a valid chain. // See the explanation of error prioritization in pkix.h.
rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
requiredKeyUsageIfPresent,
requiredEKUIfPresent, requiredPolicy,
subCACount, trustLevel);
Result deferredEndEntityError = Success; if (rv != Success) { if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
trustLevel != TrustLevel::TrustAnchor) {
deferredEndEntityError = rv;
} else { return rv;
}
}
if (trustLevel == TrustLevel::TrustAnchor) { // End of the recursion.
// This must be done here, after the chain is built but before any // revocation checks have been done. return trustDomain.IsChainValid(chain, time, requiredPolicy);
}
if (subject.endEntityOrCA == EndEntityOrCA::MustBeCA) { // Avoid stack overflows and poor performance by limiting cert chain // length. staticconstunsignedint MAX_SUBCA_COUNT = 6;
static_assert(1/*end-entity*/ + MAX_SUBCA_COUNT + 1/*root*/ ==
NonOwningDERArray::MAX_LENGTH, "MAX_SUBCA_COUNT and NonOwningDERArray::MAX_LENGTH mismatch."); if (subCACount >= MAX_SUBCA_COUNT) { return Result::ERROR_UNKNOWN_ISSUER;
}
++subCACount;
} else {
assert(subCACount == 0);
}
// Find a trusted issuer.
PathBuildingStep pathBuilder(trustDomain, subject, time,
requiredEKUIfPresent, requiredPolicy,
stapledOCSPResponse, subCACount,
deferredEndEntityError, buildForwardCallBudget);
// If we found a valid chain but deferred reporting an error with the // end-entity certificate, report it now. if (deferredEndEntityError != Success) { return deferredEndEntityError;
}
// We've built a valid chain from the subject cert up to a trusted root. return Success;
}
Result
BuildCertChain(TrustDomain& trustDomain, Input certDER,
Time time, EndEntityOrCA endEntityOrCA,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent, const CertPolicyId& requiredPolicy, /*optional*/ const Input* stapledOCSPResponse)
{ // XXX: Support the legacy use of the subject CN field for indicating the // domain name the certificate is valid for.
BackCert cert(certDER, endEntityOrCA, nullptr);
Result rv = cert.Init(); if (rv != Success) { return rv;
}
// See bug 1056341 for context. If mozilla::pkix is being used in an // environment where there are many certificates that all have the same // distinguished name as their subject and issuer (but different SPKIs - see // the loop prevention as per RFC4158 Section 5.2 in PathBuildingStep::Check), // the space to search becomes exponential. Because it would be prohibitively // expensive to explore the entire space, we introduce a budget here that, // when exhausted, terminates the search with the result // Result::ERROR_UNKNOWN_ISSUER. Essentially, we limit the total number of // times `BuildForward` can be called. The current value appears to be a good // balance between finding a path when one exists (when the space isn't too // large) and timing out quickly enough when the space is too large or there // is no valid path to a trust anchor. unsignedint buildForwardCallBudget = 200000; return BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent,
requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse, 0/*subCACount*/, buildForwardCallBudget);
}
} } // namespace mozilla::pkix
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-10-02)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.