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 documentation for CheckPublicKey() in pkixtypes.h for why the public // key also needs to be checked here when trustDomain.VerifySignedData() // should already be doing it. rv = trustDomain.CheckPublicKey(cert.GetSubjectPublicKeyInfo()); if (rv != Success) { return rv; } return BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent, requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse, 0/*subCACount*/); }
// The code that executes in the inner loop of BuildForward static Result BuildForwardInner(TrustDomain& trustDomain, BackCert& subject, PRTime time, KeyPurposeId requiredEKUIfPresent, const CertPolicyId& requiredPolicy, const SECItem& potentialIssuerDER, unsigned int subCACount, ScopedCERTCertList& results) { BackCert potentialIssuer(&subject, BackCert::IncludeCN::No); Result rv = potentialIssuer.Init(potentialIssuerDER); if (rv != Success) { return rv; } // RFC5280 4.2.1.1. Authority Key Identifier // RFC5280 4.2.1.2. Subject Key Identifier // 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. bool loopDetected = false; for (BackCert* prev = potentialIssuer.childCert; !loopDetected && prev != nullptr; prev = prev->childCert) { if (SECITEM_ItemsAreEqual(&potentialIssuer.GetSubjectPublicKeyInfo(), &prev->GetSubjectPublicKeyInfo()) && SECITEM_ItemsAreEqual(&potentialIssuer.GetSubject(), &prev->GetSubject())) { return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER); // XXX: error code } } rv = CheckNameConstraints(potentialIssuer); if (rv != Success) { return rv; } rv = BuildForward(trustDomain, potentialIssuer, time, EndEntityOrCA::MustBeCA, KU_KEY_CERT_SIGN, requiredEKUIfPresent, requiredPolicy, nullptr, subCACount, results); if (rv != Success) { return rv; } return subject.VerifyOwnSignatureWithKey( trustDomain, potentialIssuer.GetSubjectPublicKeyInfo()); }
SECStatus BuildCertChain(TrustDomain& trustDomain, CERTCertificate* certToDup, PRTime time, EndEntityOrCA endEntityOrCA, /*optional*/ KeyUsages requiredKeyUsagesIfPresent, /*optional*/ SECOidTag requiredEKUIfPresent, /*optional*/ SECOidTag requiredPolicy, /*optional*/ const SECItem* stapledOCSPResponse, /*out*/ ScopedCERTCertList& results) { PORT_Assert(certToDup); if (!certToDup) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } // The only non-const operation on the cert we are allowed to do is // CERT_DupCertificate. // XXX: Support the legacy use of the subject CN field for indicating the // domain name the certificate is valid for. BackCert::ConstrainedNameOptions cnOptions = endEntityOrCA == MustBeEndEntity && requiredEKUIfPresent == SEC_OID_EXT_KEY_USAGE_SERVER_AUTH ? BackCert::IncludeCN : BackCert::ExcludeCN; BackCert cert(certToDup, nullptr, cnOptions); Result rv = cert.Init(); if (rv != Success) { return SECFailure; } rv = BuildForward(trustDomain, cert, time, endEntityOrCA, requiredKeyUsagesIfPresent, requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse, 0, results); if (rv != Success) { results = nullptr; return SECFailure; } return SECSuccess; }
SECStatus BuildCertChain(TrustDomain& trustDomain, const CERTCertificate* nssCert, PRTime time, EndEntityOrCA endEntityOrCA, /*optional*/ KeyUsages requiredKeyUsagesIfPresent, /*optional*/ KeyPurposeId requiredEKUIfPresent, const CertPolicyId& requiredPolicy, /*optional*/ const SECItem* stapledOCSPResponse, /*out*/ ScopedCERTCertList& results) { if (!nssCert) { PR_NOT_REACHED("null cert passed to BuildCertChain"); PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } // XXX: Support the legacy use of the subject CN field for indicating the // domain name the certificate is valid for. BackCert::IncludeCN includeCN = endEntityOrCA == EndEntityOrCA::MustBeEndEntity && requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth ? BackCert::IncludeCN::Yes : BackCert::IncludeCN::No; BackCert cert(nullptr, includeCN); Result rv = cert.Init(nssCert->derCert); if (rv != Success) { return SECFailure; } rv = BuildForward(trustDomain, cert, time, endEntityOrCA, requiredKeyUsagesIfPresent, requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse, 0, results); if (rv != Success) { results = nullptr; return SECFailure; } return SECSuccess; }
// 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); } // RFC5280 4.2.1.1. Authority Key Identifier // RFC5280 4.2.1.2. Subject Key Identifier // 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. bool loopDetected = false; for (const BackCert* prev = potentialIssuer.childCert; !loopDetected && prev != nullptr; 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); } } // 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); if (rv != Success) { return RecordResult(rv, keepGoing); } rv = trustDomain.VerifySignedData(subject.GetSignedData(), potentialIssuer.GetSubjectPublicKeyInfo()); if (rv != Success) { return RecordResult(rv, keepGoing); } CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(), subject.GetSerialNumber()); rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time, stapledOCSPResponse, subject.GetAuthorityInfoAccess()); if (rv != Success) { return RecordResult(rv, keepGoing); } return RecordResult(Success, keepGoing); }
// The code that executes in the inner loop of BuildForward static Result BuildForwardInner(TrustDomain& trustDomain, BackCert& subject, PRTime time, EndEntityOrCA endEntityOrCA, SECOidTag requiredEKUIfPresent, SECOidTag requiredPolicy, CERTCertificate* potentialIssuerCertToDup, /*optional*/ const SECItem* stapledOCSPResponse, unsigned int subCACount, ScopedCERTCertList& results) { PORT_Assert(potentialIssuerCertToDup); BackCert potentialIssuer(potentialIssuerCertToDup, &subject, BackCert::ExcludeCN); Result rv = potentialIssuer.Init(); if (rv != Success) { return rv; } // RFC5280 4.2.1.1. Authority Key Identifier // RFC5280 4.2.1.2. Subject Key Identifier // 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. bool loopDetected = false; for (BackCert* prev = potentialIssuer.childCert; !loopDetected && prev != nullptr; prev = prev->childCert) { if (SECITEM_ItemsAreEqual(&potentialIssuer.GetNSSCert()->derPublicKey, &prev->GetNSSCert()->derPublicKey) && SECITEM_ItemsAreEqual(&potentialIssuer.GetNSSCert()->derSubject, &prev->GetNSSCert()->derSubject)) { return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER); // XXX: error code } } rv = CheckNameConstraints(potentialIssuer); if (rv != Success) { return rv; } unsigned int newSubCACount = subCACount; if (endEntityOrCA == MustBeCA) { newSubCACount = subCACount + 1; } else { PR_ASSERT(newSubCACount == 0); } rv = BuildForward(trustDomain, potentialIssuer, time, MustBeCA, KU_KEY_CERT_SIGN, requiredEKUIfPresent, requiredPolicy, nullptr, newSubCACount, results); if (rv != Success) { return rv; } if (trustDomain.VerifySignedData(&subject.GetNSSCert()->signatureWrap, potentialIssuer.GetNSSCert()) != SECSuccess) { return MapSECStatus(SECFailure); } return Success; }