blob: 10456857c55d5d9276fd5144ca8e3dd502fed792 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/cert/cert_verify_proc_builtin.h"
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/network_time/time_tracker/time_tracker.h"
#include "crypto/sha2.h"
#include "net/base/features.h"
#include "net/base/ip_address.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_net_fetcher.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/cert_verify_proc.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/ct_policy_enforcer.h"
#include "net/cert/ct_policy_status.h"
#include "net/cert/ct_verifier.h"
#include "net/cert/ev_root_ca_metadata.h"
#include "net/cert/internal/cert_issuer_source_aia.h"
#include "net/cert/internal/revocation_checker.h"
#include "net/cert/internal/system_trust_store.h"
#include "net/cert/signed_certificate_timestamp_and_status.h"
#include "net/cert/test_root_certs.h"
#include "net/cert/time_conversions.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/log/net_log_values.h"
#include "net/log/net_log_with_source.h"
#include "third_party/boringssl/src/pki/cert_errors.h"
#include "third_party/boringssl/src/pki/cert_issuer_source_static.h"
#include "third_party/boringssl/src/pki/common_cert_errors.h"
#include "third_party/boringssl/src/pki/name_constraints.h"
#include "third_party/boringssl/src/pki/parsed_certificate.h"
#include "third_party/boringssl/src/pki/path_builder.h"
#include "third_party/boringssl/src/pki/simple_path_builder_delegate.h"
#include "third_party/boringssl/src/pki/trust_store.h"
#include "third_party/boringssl/src/pki/trust_store_collection.h"
#include "third_party/boringssl/src/pki/trust_store_in_memory.h"
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
#include "base/version_info/version_info.h" // nogncheck
#include "net/cert/internal/trust_store_chrome.h"
#endif
using bssl::CertErrorId;
namespace net {
namespace {
// To avoid a denial-of-service risk, cap iterations by the path builder.
// Without a limit, path building is potentially exponential. This limit was
// set based on UMA histograms in the wild. See https://crrev.com/c/4903550.
//
// TODO(crbug.com/41267856): Move this limit into BoringSSL as a default.
constexpr uint32_t kPathBuilderIterationLimit = 20;
constexpr base::TimeDelta kMaxVerificationTime = base::Seconds(60);
constexpr base::TimeDelta kPerAttemptMinVerificationTimeLimit =
base::Seconds(5);
DEFINE_CERT_ERROR_ID(kPathLacksEVPolicy, "Path does not have an EV policy");
DEFINE_CERT_ERROR_ID(kChromeRootConstraintsFailed,
"Path does not satisfy CRS constraints");
base::Value::Dict NetLogCertParams(const CRYPTO_BUFFER* cert_handle,
const bssl::CertErrors& errors) {
base::Value::Dict results;
std::string pem_encoded;
if (X509Certificate::GetPEMEncodedFromDER(
x509_util::CryptoBufferAsStringPiece(cert_handle), &pem_encoded)) {
results.Set("certificate", pem_encoded);
}
std::string errors_string = errors.ToDebugString();
if (!errors_string.empty())
results.Set("errors", errors_string);
return results;
}
base::Value::Dict NetLogAdditionalCert(const CRYPTO_BUFFER* cert_handle,
const bssl::CertificateTrust& trust,
const bssl::CertErrors& errors) {
base::Value::Dict results = NetLogCertParams(cert_handle, errors);
results.Set("trust", trust.ToDebugString());
return results;
}
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
base::Value::Dict NetLogChromeRootStoreVersion(
int64_t chrome_root_store_version) {
base::Value::Dict results;
results.Set("version_major", NetLogNumberValue(chrome_root_store_version));
return results;
}
#endif
base::Value::List PEMCertValueList(const bssl::ParsedCertificateList& certs) {
base::Value::List value;
for (const auto& cert : certs) {
std::string pem;
X509Certificate::GetPEMEncodedFromDER(cert->der_cert().AsStringView(),
&pem);
value.Append(std::move(pem));
}
return value;
}
base::Value::Dict NetLogPathBuilderResultPath(
const bssl::CertPathBuilderResultPath& result_path) {
base::Value::Dict dict;
dict.Set("is_valid", result_path.IsValid());
dict.Set("last_cert_trust", result_path.last_cert_trust.ToDebugString());
dict.Set("certificates", PEMCertValueList(result_path.certs));
// TODO(crbug.com/40479281): netlog user_constrained_policy_set.
std::string errors_string =
result_path.errors.ToDebugString(result_path.certs);
if (!errors_string.empty())
dict.Set("errors", errors_string);
return dict;
}
base::Value::Dict NetLogPathBuilderResult(
const bssl::CertPathBuilder::Result& result) {
base::Value::Dict dict;
// TODO(crbug.com/40479281): include debug data (or just have things netlog it
// directly).
dict.Set("has_valid_path", result.HasValidPath());
dict.Set("best_result_index", static_cast<int>(result.best_result_index));
if (result.exceeded_iteration_limit)
dict.Set("exceeded_iteration_limit", true);
if (result.exceeded_deadline)
dict.Set("exceeded_deadline", true);
return dict;
}
RevocationPolicy NoRevocationChecking() {
RevocationPolicy policy;
policy.check_revocation = false;
policy.networking_allowed = false;
policy.crl_allowed = false;
policy.allow_missing_info = true;
policy.allow_unable_to_check = true;
policy.enforce_baseline_requirements = false;
return policy;
}
// Gets the set of policy OIDs in |cert| that are recognized as EV OIDs for some
// root.
void GetEVPolicyOids(const EVRootCAMetadata* ev_metadata,
const bssl::ParsedCertificate* cert,
std::set<bssl::der::Input>* oids) {
oids->clear();
if (!cert->has_policy_oids())
return;
for (const bssl::der::Input& oid : cert->policy_oids()) {
if (ev_metadata->IsEVPolicyOID(oid)) {
oids->insert(oid);
}
}
}
// Returns true if |cert| could be an EV certificate, based on its policies
// extension. A return of false means it definitely is not an EV certificate,
// whereas a return of true means it could be EV.
bool IsEVCandidate(const EVRootCAMetadata* ev_metadata,
const bssl::ParsedCertificate* cert) {
std::set<bssl::der::Input> oids;
GetEVPolicyOids(ev_metadata, cert, &oids);
return !oids.empty();
}
// CertVerifyProcTrustStore wraps a SystemTrustStore with additional trust
// anchors and TestRootCerts.
class CertVerifyProcTrustStore {
public:
// |system_trust_store| must outlive this object.
explicit CertVerifyProcTrustStore(
SystemTrustStore* system_trust_store,
bssl::TrustStoreInMemory* additional_trust_store)
: system_trust_store_(system_trust_store),
additional_trust_store_(additional_trust_store) {
trust_store_.AddTrustStore(additional_trust_store_);
trust_store_.AddTrustStore(system_trust_store_->GetTrustStore());
// When running in test mode, also layer in the test-only root certificates.
//
// Note that this integration requires TestRootCerts::HasInstance() to be
// true by the time CertVerifyProcTrustStore is created - a limitation which
// is acceptable for the test-only code that consumes this.
if (TestRootCerts::HasInstance()) {
trust_store_.AddTrustStore(
TestRootCerts::GetInstance()->test_trust_store());
}
}
bssl::TrustStore* trust_store() { return &trust_store_; }
bool IsKnownRoot(const bssl::ParsedCertificate* trust_anchor) const {
if (TestRootCerts::HasInstance() &&
TestRootCerts::GetInstance()->IsKnownRoot(trust_anchor->der_cert())) {
return true;
}
return system_trust_store_->IsKnownRoot(trust_anchor);
}
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
base::span<const ChromeRootCertConstraints> GetChromeRootConstraints(
const bssl::ParsedCertificate* cert) const {
return system_trust_store_->GetChromeRootConstraints(cert);
}
bool IsNonChromeRootStoreTrustAnchor(
const bssl::ParsedCertificate* trust_anchor) const {
return additional_trust_store_->GetTrust(trust_anchor).IsTrustAnchor() ||
system_trust_store_->IsLocallyTrustedRoot(trust_anchor);
}
#endif
private:
raw_ptr<SystemTrustStore> system_trust_store_;
raw_ptr<bssl::TrustStoreInMemory> additional_trust_store_;
bssl::TrustStoreCollection trust_store_;
};
// Enum for whether path building is attempting to verify a certificate as EV or
// as DV.
enum class VerificationType {
kEV, // Extended Validation
kDV, // Domain Validation
};
class PathBuilderDelegateDataImpl : public bssl::CertPathBuilderDelegateData {
public:
~PathBuilderDelegateDataImpl() override = default;
static const PathBuilderDelegateDataImpl* Get(
const bssl::CertPathBuilderResultPath& path) {
return static_cast<PathBuilderDelegateDataImpl*>(path.delegate_data.get());
}
static PathBuilderDelegateDataImpl* GetOrCreate(
bssl::CertPathBuilderResultPath* path) {
if (!path->delegate_data)
path->delegate_data = std::make_unique<PathBuilderDelegateDataImpl>();
return static_cast<PathBuilderDelegateDataImpl*>(path->delegate_data.get());
}
bssl::OCSPVerifyResult stapled_ocsp_verify_result;
SignedCertificateTimestampAndStatusList scts;
ct::CTPolicyCompliance ct_policy_compliance;
};
// TODO(eroman): The path building code in this file enforces its idea of weak
// keys, and signature algorithms, but separately cert_verify_proc.cc also
// checks the chains with its own policy. These policies must be aligned to
// give path building the best chance of finding a good path.
class PathBuilderDelegateImpl : public bssl::SimplePathBuilderDelegate {
public:
// Uses the default policy from bssl::SimplePathBuilderDelegate, which
// requires RSA keys to be at least 1024-bits large, and optionally accepts
// SHA1 certificates.
PathBuilderDelegateImpl(
const CRLSet* crl_set,
CTVerifier* ct_verifier,
const CTPolicyEnforcer* ct_policy_enforcer,
CertNetFetcher* net_fetcher,
VerificationType verification_type,
bssl::SimplePathBuilderDelegate::DigestPolicy digest_policy,
int flags,
const CertVerifyProcTrustStore* trust_store,
const std::vector<net::CertVerifyProc::CertificateWithConstraints>&
additional_constraints,
std::string_view stapled_leaf_ocsp_response,
std::string_view sct_list_from_tls_extension,
const EVRootCAMetadata* ev_metadata,
base::TimeTicks deadline,
base::Time current_time,
bool* checked_revocation_for_some_path,
const NetLogWithSource& net_log)
: bssl::SimplePathBuilderDelegate(1024, digest_policy),
crl_set_(crl_set),
ct_verifier_(ct_verifier),
ct_policy_enforcer_(ct_policy_enforcer),
net_fetcher_(net_fetcher),
verification_type_(verification_type),
flags_(flags),
trust_store_(trust_store),
additional_constraints_(additional_constraints),
stapled_leaf_ocsp_response_(stapled_leaf_ocsp_response),
sct_list_from_tls_extension_(sct_list_from_tls_extension),
ev_metadata_(ev_metadata),
deadline_(deadline),
current_time_(current_time),
checked_revocation_for_some_path_(checked_revocation_for_some_path),
net_log_(net_log) {}
// This is called for each built chain, including ones which failed. It is
// responsible for adding errors to the built chain if it is not acceptable.
void CheckPathAfterVerification(
const bssl::CertPathBuilder& path_builder,
bssl::CertPathBuilderResultPath* path) override {
net_log_->BeginEvent(NetLogEventType::CERT_VERIFY_PROC_PATH_BUILT);
CheckPathAfterVerificationImpl(path_builder, path);
net_log_->EndEvent(NetLogEventType::CERT_VERIFY_PROC_PATH_BUILT,
[&] { return NetLogPathBuilderResultPath(*path); });
}
private:
void CheckPathAfterVerificationImpl(const bssl::CertPathBuilder& path_builder,
bssl::CertPathBuilderResultPath* path) {
PathBuilderDelegateDataImpl* delegate_data =
PathBuilderDelegateDataImpl::GetOrCreate(path);
// TODO(https://crbug.com/1211074, https://crbug.com/848277): making a
// temporary X509Certificate just to pass into CTVerifier and
// CTPolicyEnforcer is silly, refactor so they take CRYPTO_BUFFER or
// ParsedCertificate or something.
std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
if (path->certs.size() > 1) {
intermediates.push_back(bssl::UpRef(path->certs[1]->cert_buffer()));
}
auto cert_for_ct_verify = X509Certificate::CreateFromBuffer(
bssl::UpRef(path->certs[0]->cert_buffer()), std::move(intermediates));
ct_verifier_->Verify(cert_for_ct_verify.get(), stapled_leaf_ocsp_response_,
sct_list_from_tls_extension_, current_time_,
&delegate_data->scts, *net_log_);
// Check any extra constraints that might exist outside of the certificates.
CheckExtraConstraints(path->certs, &path->errors);
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
CheckChromeRootConstraints(path);
#endif
// If the path is already invalid, don't check revocation status. The
// chain is expected to be valid when doing revocation checks (since for
// instance the correct issuer for a certificate may need to be known).
// Also if certificates are already expired, obtaining their revocation
// status may fail.
//
// TODO(eroman): When CertVerifyProcBuiltin fails to find a valid path,
// whatever (partial/incomplete) path it does return should
// minimally be checked with the CRLSet.
if (!path->IsValid()) {
return;
}
// If EV was requested the certificate must chain to a recognized EV root
// and have one of its recognized EV policy OIDs.
if (verification_type_ == VerificationType::kEV) {
if (!ConformsToEVPolicy(path)) {
path->errors.GetErrorsForCert(0)->AddError(kPathLacksEVPolicy);
return;
}
}
// Select an appropriate revocation policy for this chain based on the
// verifier flags and root.
RevocationPolicy policy = ChooseRevocationPolicy(path->certs);
// Check for revocations using the CRLSet.
switch (
CheckChainRevocationUsingCRLSet(crl_set_, path->certs, &path->errors)) {
case CRLSet::Result::REVOKED:
return;
case CRLSet::Result::GOOD:
break;
case CRLSet::Result::UNKNOWN:
// CRLSet was inconclusive.
break;
}
if (policy.check_revocation) {
*checked_revocation_for_some_path_ = true;
}
// Check the revocation status for each certificate in the chain according
// to |policy|. Depending on the policy, errors will be added to the
// respective certificates, so |errors->ContainsHighSeverityErrors()| will
// reflect the revocation status of the chain after this call.
CheckValidatedChainRevocation(path->certs, policy, deadline_,
stapled_leaf_ocsp_response_, current_time_,
net_fetcher_, &path->errors,
&delegate_data->stapled_ocsp_verify_result);
ct::SCTList verified_scts;
for (const auto& sct_and_status : delegate_data->scts) {
if (sct_and_status.status == ct::SCT_STATUS_OK) {
verified_scts.push_back(sct_and_status.sct);
}
}
delegate_data->ct_policy_compliance = ct_policy_enforcer_->CheckCompliance(
cert_for_ct_verify.get(), verified_scts, current_time_, *net_log_);
}
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
// Returns the SCTs from `scts` that are verified successfully and signed by
// a log which was not disqualified.
ct::SCTList ValidScts(const SignedCertificateTimestampAndStatusList& scts) {
ct::SCTList valid_scts;
for (const auto& sct_and_status : scts) {
if (sct_and_status.status != ct::SCT_STATUS_OK) {
continue;
}
std::optional<base::Time> disqualification_time =
ct_policy_enforcer_->GetLogDisqualificationTime(
sct_and_status.sct->log_id);
// TODO(https://crbug.com/40840044): use the same time source here as for
// the rest of verification.
if (disqualification_time && base::Time::Now() >= disqualification_time) {
continue;
}
valid_scts.push_back(sct_and_status.sct);
}
return valid_scts;
}
bool CheckPathSatisfiesChromeRootConstraint(
bssl::CertPathBuilderResultPath* path,
const ChromeRootCertConstraints& constraint) {
PathBuilderDelegateDataImpl* delegate_data =
PathBuilderDelegateDataImpl::GetOrCreate(path);
// TODO(https://crbug.com/40941039): add more specific netlog or CertError
// logs about which constraint failed exactly? (Note that it could be
// confusing when there are multiple ChromeRootCertConstraints objects,
// would need to clearly distinguish which set of constraints had errors.)
if (ct_policy_enforcer_->IsCtEnabled()) {
if (constraint.sct_not_after.has_value()) {
bool found_matching_sct = false;
for (const auto& sct : ValidScts(delegate_data->scts)) {
if (sct->timestamp <= constraint.sct_not_after.value()) {
found_matching_sct = true;
break;
}
}
if (!found_matching_sct) {
return false;
}
}
if (constraint.sct_all_after.has_value()) {
ct::SCTList valid_scts = ValidScts(delegate_data->scts);
if (valid_scts.empty()) {
return false;
}
for (const auto& sct : ValidScts(delegate_data->scts)) {
if (sct->timestamp <= constraint.sct_all_after.value()) {
return false;
}
}
}
}
if (!constraint.permitted_dns_names.empty()) {
bssl::GeneralNames permitted_names;
for (const auto& dns_name : constraint.permitted_dns_names) {
permitted_names.dns_names.push_back(dns_name);
}
permitted_names.present_name_types |=
bssl::GeneralNameTypes::GENERAL_NAME_DNS_NAME;
std::unique_ptr<bssl::NameConstraints> nc =
bssl::NameConstraints::CreateFromPermittedSubtrees(
std::move(permitted_names));
const std::shared_ptr<const bssl::ParsedCertificate>& leaf_cert =
path->certs[0];
bssl::CertErrors name_constraint_errors;
nc->IsPermittedCert(leaf_cert->normalized_subject(),
leaf_cert->subject_alt_names(),
&name_constraint_errors);
if (name_constraint_errors.ContainsAnyErrorWithSeverity(
bssl::CertError::SEVERITY_HIGH)) {
return false;
}
}
if (constraint.min_version.has_value() &&
version_info::GetVersion() < constraint.min_version.value()) {
return false;
}
if (constraint.max_version_exclusive.has_value() &&
version_info::GetVersion() >=
constraint.max_version_exclusive.value()) {
return false;
}
return true;
}
void CheckChromeRootConstraints(bssl::CertPathBuilderResultPath* path) {
// If the root is trusted locally, do not enforce CRS constraints, even if
// some exist.
if (trust_store_->IsNonChromeRootStoreTrustAnchor(
path->certs.back().get())) {
return;
}
if (base::span<const ChromeRootCertConstraints> constraints =
trust_store_->GetChromeRootConstraints(path->certs.back().get());
!constraints.empty()) {
bool found_valid_constraint = false;
for (const ChromeRootCertConstraints& constraint : constraints) {
found_valid_constraint |=
CheckPathSatisfiesChromeRootConstraint(path, constraint);
}
if (!found_valid_constraint) {
path->errors.GetOtherErrors()->AddError(kChromeRootConstraintsFailed);
}
}
}
#endif
// Check extra constraints that aren't encoded in the certificates themselves.
void CheckExtraConstraints(const bssl::ParsedCertificateList& certs,
bssl::CertPathErrors* errors) {
const std::shared_ptr<const bssl::ParsedCertificate> root_cert =
certs.back();
// An assumption being made is that there will be at most a few (2-3) certs
// in here; if there are more and this ends up being a drag on performance
// it may be worth making additional_constraints_ into a map storing certs
// by hash.
for (const auto& cert_with_constraints : *additional_constraints_) {
if (!x509_util::CryptoBufferEqual(
root_cert->cert_buffer(),
cert_with_constraints.certificate->cert_buffer())) {
continue;
}
// Found the cert, check constraints
if (cert_with_constraints.permitted_dns_names.empty() &&
cert_with_constraints.permitted_cidrs.empty()) {
// No constraints to check.
return;
}
bssl::GeneralNames permitted_names;
if (!cert_with_constraints.permitted_dns_names.empty()) {
for (const auto& dns_name : cert_with_constraints.permitted_dns_names) {
permitted_names.dns_names.push_back(dns_name);
}
permitted_names.present_name_types |=
bssl::GeneralNameTypes::GENERAL_NAME_DNS_NAME;
}
if (!cert_with_constraints.permitted_cidrs.empty()) {
for (const auto& cidr : cert_with_constraints.permitted_cidrs) {
permitted_names.ip_address_ranges.emplace_back(cidr.ip.bytes(),
cidr.mask.bytes());
}
permitted_names.present_name_types |=
bssl::GeneralNameTypes::GENERAL_NAME_IP_ADDRESS;
}
std::unique_ptr<bssl::NameConstraints> nc =
bssl::NameConstraints::CreateFromPermittedSubtrees(
std::move(permitted_names));
const std::shared_ptr<const bssl::ParsedCertificate>& leaf_cert =
certs[0];
nc->IsPermittedCert(leaf_cert->normalized_subject(),
leaf_cert->subject_alt_names(),
errors->GetErrorsForCert(0));
return;
}
}
// Selects a revocation policy based on the CertVerifier flags and the given
// certificate chain.
RevocationPolicy ChooseRevocationPolicy(
const bssl::ParsedCertificateList& certs) {
if (flags_ & CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES) {
// In theory when network fetches are disabled but revocation is enabled
// we could continue with networking_allowed=false (and
// VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS would also have to change
// allow_missing_info and allow_unable_to_check to true).
// That theoretically could allow still consulting any cached CRLs/etc.
// However in the way things are currently implemented in the builtin
// verifier there really is no point to bothering, just disable
// revocation checking if network fetches are disabled.
return NoRevocationChecking();
}
// Use hard-fail revocation checking for local trust anchors, if requested
// by the load flag and the chain uses a non-public root.
if ((flags_ & CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS) &&
!certs.empty() && !trust_store_->IsKnownRoot(certs.back().get())) {
RevocationPolicy policy;
policy.check_revocation = true;
policy.networking_allowed = true;
policy.crl_allowed = true;
policy.allow_missing_info = false;
policy.allow_unable_to_check = false;
policy.enforce_baseline_requirements = false;
return policy;
}
// Use soft-fail revocation checking for VERIFY_REV_CHECKING_ENABLED.
if (flags_ & CertVerifyProc::VERIFY_REV_CHECKING_ENABLED) {
const bool is_known_root =
!certs.empty() && trust_store_->IsKnownRoot(certs.back().get());
RevocationPolicy policy;
policy.check_revocation = true;
policy.networking_allowed = true;
// Publicly trusted certs are required to have OCSP by the Baseline
// Requirements and CRLs can be quite large, so disable the fallback to
// CRLs for chains to known roots.
policy.crl_allowed = !is_known_root;
policy.allow_missing_info = true;
policy.allow_unable_to_check = true;
policy.enforce_baseline_requirements = is_known_root;
return policy;
}
return NoRevocationChecking();
}
// Returns true if |path| chains to an EV root, and the chain conforms to
// one of its EV policy OIDs. When building paths all candidate EV policy
// OIDs were requested, so it is just a matter of testing each of the
// policies the chain conforms to.
bool ConformsToEVPolicy(const bssl::CertPathBuilderResultPath* path) {
const bssl::ParsedCertificate* root = path->GetTrustedCert();
if (!root) {
return false;
}
SHA256HashValue root_fingerprint;
crypto::SHA256HashString(root->der_cert().AsStringView(),
root_fingerprint.data,
sizeof(root_fingerprint.data));
for (const bssl::der::Input& oid : path->user_constrained_policy_set) {
if (ev_metadata_->HasEVPolicyOID(root_fingerprint, oid)) {
return true;
}
}
return false;
}
bool IsDeadlineExpired() override {
return !deadline_.is_null() && base::TimeTicks::Now() > deadline_;
}
bool IsDebugLogEnabled() override { return net_log_->IsCapturing(); }
void DebugLog(std::string_view msg) override {
net_log_->AddEventWithStringParams(
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILDER_DEBUG, "debug", msg);
}
raw_ptr<const CRLSet> crl_set_;
raw_ptr<CTVerifier> ct_verifier_;
raw_ptr<const CTPolicyEnforcer> ct_policy_enforcer_;
raw_ptr<CertNetFetcher> net_fetcher_;
const VerificationType verification_type_;
const int flags_;
raw_ptr<const CertVerifyProcTrustStore> trust_store_;
raw_ref<const std::vector<net::CertVerifyProc::CertificateWithConstraints>>
additional_constraints_;
const std::string_view stapled_leaf_ocsp_response_;
const std::string_view sct_list_from_tls_extension_;
raw_ptr<const EVRootCAMetadata> ev_metadata_;
base::TimeTicks deadline_;
base::Time current_time_;
raw_ptr<bool> checked_revocation_for_some_path_;
raw_ref<const NetLogWithSource> net_log_;
};
std::shared_ptr<const bssl::ParsedCertificate> ParseCertificateFromBuffer(
CRYPTO_BUFFER* cert_handle,
bssl::CertErrors* errors) {
return bssl::ParsedCertificate::Create(
bssl::UpRef(cert_handle), x509_util::DefaultParseCertificateOptions(),
errors);
}
class CertVerifyProcBuiltin : public CertVerifyProc {
public:
CertVerifyProcBuiltin(scoped_refptr<CertNetFetcher> net_fetcher,
scoped_refptr<CRLSet> crl_set,
std::unique_ptr<CTVerifier> ct_verifier,
scoped_refptr<CTPolicyEnforcer> ct_policy_enforcer,
std::unique_ptr<SystemTrustStore> system_trust_store,
const CertVerifyProc::InstanceParams& instance_params,
std::optional<network_time::TimeTracker> time_tracker);
protected:
~CertVerifyProcBuiltin() override;
private:
int VerifyInternal(X509Certificate* cert,
const std::string& hostname,
const std::string& ocsp_response,
const std::string& sct_list,
int flags,
CertVerifyResult* verify_result,
const NetLogWithSource& net_log) override;
const scoped_refptr<CertNetFetcher> net_fetcher_;
const std::unique_ptr<CTVerifier> ct_verifier_;
const scoped_refptr<CTPolicyEnforcer> ct_policy_enforcer_;
const std::unique_ptr<SystemTrustStore> system_trust_store_;
std::vector<net::CertVerifyProc::CertificateWithConstraints>
additional_constraints_;
bssl::TrustStoreInMemory additional_trust_store_;
const std::optional<network_time::TimeTracker> time_tracker_;
};
CertVerifyProcBuiltin::CertVerifyProcBuiltin(
scoped_refptr<CertNetFetcher> net_fetcher,
scoped_refptr<CRLSet> crl_set,
std::unique_ptr<CTVerifier> ct_verifier,
scoped_refptr<CTPolicyEnforcer> ct_policy_enforcer,
std::unique_ptr<SystemTrustStore> system_trust_store,
const CertVerifyProc::InstanceParams& instance_params,
std::optional<network_time::TimeTracker> time_tracker)
: CertVerifyProc(std::move(crl_set)),
net_fetcher_(std::move(net_fetcher)),
ct_verifier_(std::move(ct_verifier)),
ct_policy_enforcer_(std::move(ct_policy_enforcer)),
system_trust_store_(std::move(system_trust_store)),
time_tracker_(std::move(time_tracker)) {
DCHECK(system_trust_store_);
NetLogWithSource net_log =
NetLogWithSource::Make(net::NetLogSourceType::CERT_VERIFY_PROC_CREATED);
net_log.BeginEvent(NetLogEventType::CERT_VERIFY_PROC_CREATED);
// When adding additional certs from instance params, there needs to be a
// priority order if a cert is added with multiple different trust types.
//
// The priority is as follows:
//
// (a) Distrusted SPKIs (though we don't check for SPKI collisions in added
// certs; we rely on that to happen in path building).
// (b) Trusted certs with enforced constraints both in the cert and
// specified externally outside of the cert.
// (c) Trusted certs with enforced constraints only within the cert.
// (d) Trusted certs w/o enforced constraints.
// (e) Unspecified certs.
//
// No effort was made to categorize what applies if a cert is specified
// within the same category multiple times.
for (const auto& spki : instance_params.additional_distrusted_spkis) {
additional_trust_store_.AddDistrustedCertificateBySPKI(
std::string(base::as_string_view(spki)));
net_log.AddEvent(NetLogEventType::CERT_VERIFY_PROC_ADDITIONAL_CERT, [&] {
base::Value::Dict results;
results.Set("spki", NetLogBinaryValue(base::span(spki)));
results.Set("trust",
bssl::CertificateTrust::ForDistrusted().ToDebugString());
return results;
});
}
bssl::CertificateTrust anchor_trust_enforcement =
bssl::CertificateTrust::ForTrustAnchor()
.WithEnforceAnchorConstraints()
.WithEnforceAnchorExpiry();
for (const auto& cert_with_constraints :
instance_params.additional_trust_anchors_with_constraints) {
const std::shared_ptr<const bssl::ParsedCertificate>& cert =
cert_with_constraints.certificate;
additional_trust_store_.AddCertificate(cert, anchor_trust_enforcement);
additional_constraints_.push_back(cert_with_constraints);
bssl::CertErrors parsing_errors;
net_log.AddEvent(NetLogEventType::CERT_VERIFY_PROC_ADDITIONAL_CERT, [&] {
return NetLogAdditionalCert(cert->cert_buffer(),
bssl::CertificateTrust::ForTrustAnchor(),
parsing_errors);
});
}
bssl::CertificateTrust leaf_trust = bssl::CertificateTrust::ForTrustedLeaf();
for (const auto& cert_with_possible_constraints :
instance_params.additional_trust_leafs) {
const std::shared_ptr<const bssl::ParsedCertificate>& cert =
cert_with_possible_constraints.certificate;
if (!additional_trust_store_.Contains(cert.get())) {
if (!cert_with_possible_constraints.permitted_dns_names.empty() ||
!cert_with_possible_constraints.permitted_cidrs.empty()) {
additional_constraints_.push_back(cert_with_possible_constraints);
}
bssl::CertErrors parsing_errors;
additional_trust_store_.AddCertificate(cert, leaf_trust);
net_log.AddEvent(NetLogEventType::CERT_VERIFY_PROC_ADDITIONAL_CERT, [&] {
return NetLogAdditionalCert(cert->cert_buffer(), leaf_trust,
parsing_errors);
});
}
}
bssl::CertificateTrust anchor_leaf_trust =
bssl::CertificateTrust::ForTrustAnchorOrLeaf()
.WithEnforceAnchorConstraints()
.WithEnforceAnchorExpiry();
for (const auto& cert_with_possible_constraints :
instance_params.additional_trust_anchors_and_leafs) {
const std::shared_ptr<const bssl::ParsedCertificate>& cert =
cert_with_possible_constraints.certificate;
if (!additional_trust_store_.Contains(cert.get())) {
if (!cert_with_possible_constraints.permitted_dns_names.empty() ||
!cert_with_possible_constraints.permitted_cidrs.empty()) {
additional_constraints_.push_back(cert_with_possible_constraints);
}
bssl::CertErrors parsing_errors;
additional_trust_store_.AddCertificate(cert, anchor_leaf_trust);
net_log.AddEvent(NetLogEventType::CERT_VERIFY_PROC_ADDITIONAL_CERT, [&] {
return NetLogAdditionalCert(cert->cert_buffer(), anchor_leaf_trust,
parsing_errors);
});
}
}
for (const auto& cert :
instance_params.additional_trust_anchors_with_enforced_constraints) {
bssl::CertErrors parsing_errors;
if (!additional_trust_store_.Contains(cert.get())) {
additional_trust_store_.AddCertificate(cert, anchor_trust_enforcement);
net_log.AddEvent(NetLogEventType::CERT_VERIFY_PROC_ADDITIONAL_CERT, [&] {
return NetLogAdditionalCert(cert->cert_buffer(),
anchor_trust_enforcement, parsing_errors);
});
}
}
for (const auto& cert : instance_params.additional_trust_anchors) {
bssl::CertErrors parsing_errors;
// Only add if it wasn't already present in `additional_trust_store_`. This
// is for two reasons:
// (1) TrustStoreInMemory doesn't expect to contain duplicates
// (2) If the same anchor is added with enforced constraints, that takes
// precedence.
if (!additional_trust_store_.Contains(cert.get())) {
additional_trust_store_.AddTrustAnchor(cert);
}
net_log.AddEvent(NetLogEventType::CERT_VERIFY_PROC_ADDITIONAL_CERT, [&] {
return NetLogAdditionalCert(cert->cert_buffer(),
bssl::CertificateTrust::ForTrustAnchor(),
parsing_errors);
});
}
for (const auto& cert : instance_params.additional_untrusted_authorities) {
bssl::CertErrors parsing_errors;
// Only add the untrusted cert if it isn't already present in
// `additional_trust_store_`. If the same cert was already added as a
// trust anchor then adding it again as an untrusted cert can lead to it
// not being treated as a trust anchor since TrustStoreInMemory doesn't
// expect to contain duplicates.
if (!additional_trust_store_.Contains(cert.get())) {
additional_trust_store_.AddCertificateWithUnspecifiedTrust(cert);
}
net_log.AddEvent(NetLogEventType::CERT_VERIFY_PROC_ADDITIONAL_CERT, [&] {
return NetLogAdditionalCert(cert->cert_buffer(),
bssl::CertificateTrust::ForUnspecified(),
parsing_errors);
});
}
net_log.EndEvent(NetLogEventType::CERT_VERIFY_PROC_CREATED);
}
CertVerifyProcBuiltin::~CertVerifyProcBuiltin() = default;
void AddIntermediatesToIssuerSource(X509Certificate* x509_cert,
bssl::CertIssuerSourceStatic* intermediates,
const NetLogWithSource& net_log) {
for (const auto& intermediate : x509_cert->intermediate_buffers()) {
bssl::CertErrors errors;
std::shared_ptr<const bssl::ParsedCertificate> cert =
ParseCertificateFromBuffer(intermediate.get(), &errors);
// TODO(crbug.com/40479281): this duplicates the logging of the input chain
// maybe should only log if there is a parse error/warning?
net_log.AddEvent(NetLogEventType::CERT_VERIFY_PROC_INPUT_CERT, [&] {
return NetLogCertParams(intermediate.get(), errors);
});
if (cert) {
intermediates->AddCert(std::move(cert));
}
}
}
// Appends the SHA256 hashes of |spki_bytes| to |*hashes|.
// TODO(eroman): Hashes are also calculated at other times (such as when
// checking CRLSet). Consider caching to avoid recalculating (say
// in the delegate's PathInfo).
void AppendPublicKeyHashes(const bssl::der::Input& spki_bytes,
HashValueVector* hashes) {
HashValue sha256(HASH_VALUE_SHA256);
crypto::SHA256HashString(spki_bytes.AsStringView(), sha256.data(),
crypto::kSHA256Length);
hashes->push_back(sha256);
}
// Appends the SubjectPublicKeyInfo hashes for all certificates in
// |path| to |*hashes|.
void AppendPublicKeyHashes(const bssl::CertPathBuilderResultPath& path,
HashValueVector* hashes) {
for (const std::shared_ptr<const bssl::ParsedCertificate>& cert :
path.certs) {
AppendPublicKeyHashes(cert->tbs().spki_tlv, hashes);
}
}
// Sets the bits on |cert_status| for all the errors present in |errors| (the
// errors for a particular path).
void MapPathBuilderErrorsToCertStatus(const bssl::CertPathErrors& errors,
CertStatus* cert_status) {
// If there were no errors, nothing to do.
if (!errors.ContainsHighSeverityErrors())
return;
if (errors.ContainsError(bssl::cert_errors::kCertificateRevoked)) {
*cert_status |= CERT_STATUS_REVOKED;
}
if (errors.ContainsError(bssl::cert_errors::kNoRevocationMechanism)) {
*cert_status |= CERT_STATUS_NO_REVOCATION_MECHANISM;
}
if (errors.ContainsError(bssl::cert_errors::kUnableToCheckRevocation)) {
*cert_status |= CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
}
if (errors.ContainsError(bssl::cert_errors::kUnacceptablePublicKey)) {
*cert_status |= CERT_STATUS_WEAK_KEY;
}
if (errors.ContainsError(bssl::cert_errors::kValidityFailedNotAfter) ||
errors.ContainsError(bssl::cert_errors::kValidityFailedNotBefore)) {
*cert_status |= CERT_STATUS_DATE_INVALID;
}
if (errors.ContainsError(bssl::cert_errors::kDistrustedByTrustStore) ||
errors.ContainsError(bssl::cert_errors::kVerifySignedDataFailed) ||
errors.ContainsError(bssl::cert_errors::kNoIssuersFound) ||
errors.ContainsError(bssl::cert_errors::kSubjectDoesNotMatchIssuer) ||
errors.ContainsError(bssl::cert_errors::kDeadlineExceeded) ||
errors.ContainsError(bssl::cert_errors::kIterationLimitExceeded) ||
errors.ContainsError(kChromeRootConstraintsFailed)) {
*cert_status |= CERT_STATUS_AUTHORITY_INVALID;
}
// IMPORTANT: If the path was invalid for a reason that was not
// explicity checked above, set a general error. This is important as
// |cert_status| is what ultimately indicates whether verification was
// successful or not (absence of errors implies success).
if (!IsCertStatusError(*cert_status))
*cert_status |= CERT_STATUS_INVALID;
}
// Creates a X509Certificate (chain) to return as the verified result.
//
// * |target_cert|: The original X509Certificate that was passed in to
// VerifyInternal()
// * |path|: The result (possibly failed) from path building.
scoped_refptr<X509Certificate> CreateVerifiedCertChain(
X509Certificate* target_cert,
const bssl::CertPathBuilderResultPath& path) {
std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
// Skip the first certificate in the path as that is the target certificate
for (size_t i = 1; i < path.certs.size(); ++i) {
intermediates.push_back(bssl::UpRef(path.certs[i]->cert_buffer()));
}
scoped_refptr<X509Certificate> result =
target_cert->CloneWithDifferentIntermediates(std::move(intermediates));
DCHECK(result);
return result;
}
// Describes the parameters for a single path building attempt. Path building
// may be re-tried with different parameters for EV and for accepting SHA1
// certificates.
struct BuildPathAttempt {
BuildPathAttempt(VerificationType verification_type,
bssl::SimplePathBuilderDelegate::DigestPolicy digest_policy,
bool use_system_time)
: verification_type(verification_type),
digest_policy(digest_policy),
use_system_time(use_system_time) {}
BuildPathAttempt(VerificationType verification_type, bool use_system_time)
: BuildPathAttempt(verification_type,
bssl::SimplePathBuilderDelegate::DigestPolicy::kStrong,
use_system_time) {}
VerificationType verification_type;
bssl::SimplePathBuilderDelegate::DigestPolicy digest_policy;
bool use_system_time;
};
bssl::CertPathBuilder::Result TryBuildPath(
const std::shared_ptr<const bssl::ParsedCertificate>& target,
bssl::CertIssuerSourceStatic* intermediates,
CertVerifyProcTrustStore* trust_store,
const std::vector<net::CertVerifyProc::CertificateWithConstraints>&
additional_constraints,
const bssl::der::GeneralizedTime& der_verification_time,
base::Time current_time,
base::TimeTicks deadline,
VerificationType verification_type,
bssl::SimplePathBuilderDelegate::DigestPolicy digest_policy,
int flags,
std::string_view ocsp_response,
std::string_view sct_list,
const CRLSet* crl_set,
CTVerifier* ct_verifier,
const CTPolicyEnforcer* ct_policy_enforcer,
CertNetFetcher* net_fetcher,
const EVRootCAMetadata* ev_metadata,
bool* checked_revocation,
const NetLogWithSource& net_log) {
// Path building will require candidate paths to conform to at least one of
// the policies in |user_initial_policy_set|.
std::set<bssl::der::Input> user_initial_policy_set;
if (verification_type == VerificationType::kEV) {
GetEVPolicyOids(ev_metadata, target.get(), &user_initial_policy_set);
// TODO(crbug.com/40479281): netlog user_initial_policy_set.
} else {
user_initial_policy_set = {bssl::der::Input(bssl::kAnyPolicyOid)};
}
PathBuilderDelegateImpl path_builder_delegate(
crl_set, ct_verifier, ct_policy_enforcer, net_fetcher, verification_type,
digest_policy, flags, trust_store, additional_constraints, ocsp_response,
sct_list, ev_metadata, deadline, current_time, checked_revocation,
net_log);
std::optional<CertIssuerSourceAia> aia_cert_issuer_source;
// Initialize the path builder.
bssl::CertPathBuilder path_builder(
target, trust_store->trust_store(), &path_builder_delegate,
der_verification_time, bssl::KeyPurpose::SERVER_AUTH,
bssl::InitialExplicitPolicy::kFalse, user_initial_policy_set,
bssl::InitialPolicyMappingInhibit::kFalse,
bssl::InitialAnyPolicyInhibit::kFalse);
// Allow the path builder to discover the explicitly provided intermediates in
// |input_cert|.
path_builder.AddCertIssuerSource(intermediates);
// Allow the path builder to discover intermediates through AIA fetching.
// TODO(crbug.com/40479281): hook up netlog to AIA.
if (!(flags & CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES)) {
if (net_fetcher) {
aia_cert_issuer_source.emplace(net_fetcher);
path_builder.AddCertIssuerSource(&aia_cert_issuer_source.value());
} else {
VLOG(1) << "No net_fetcher for performing AIA chasing.";
}
}
path_builder.SetIterationLimit(kPathBuilderIterationLimit);
return path_builder.Run();
}
int AssignVerifyResult(X509Certificate* input_cert,
const std::string& hostname,
bssl::CertPathBuilder::Result& result,
VerificationType verification_type,
bool checked_revocation_for_some_path,
CertVerifyProcTrustStore* trust_store,
CertVerifyResult* verify_result) {
const bssl::CertPathBuilderResultPath* best_path_possibly_invalid =
result.GetBestPathPossiblyInvalid();
if (!best_path_possibly_invalid) {
// TODO(crbug.com/41267838): What errors to communicate? Maybe the path
// builder should always return some partial path (even if just containing
// the target), then there is a bssl::CertErrors to test.
verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
return ERR_CERT_AUTHORITY_INVALID;
}
const bssl::CertPathBuilderResultPath& partial_path =
*best_path_possibly_invalid;
AppendPublicKeyHashes(partial_path, &verify_result->public_key_hashes);
bool path_is_valid = partial_path.IsValid();
const bssl::ParsedCertificate* trusted_cert = partial_path.GetTrustedCert();
if (trusted_cert) {
verify_result->is_issued_by_known_root =
trust_store->IsKnownRoot(trusted_cert);
}
if (path_is_valid && (verification_type == VerificationType::kEV)) {
verify_result->cert_status |= CERT_STATUS_IS_EV;
}
// TODO(eroman): Add documentation for the meaning of
// CERT_STATUS_REV_CHECKING_ENABLED. Based on the current tests it appears to
// mean whether revocation checking was attempted during path building,
// although does not necessarily mean that revocation checking was done for
// the final returned path.
if (checked_revocation_for_some_path)
verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
verify_result->verified_cert =
CreateVerifiedCertChain(input_cert, partial_path);
MapPathBuilderErrorsToCertStatus(partial_path.errors,
&verify_result->cert_status);
// TODO(eroman): Is it possible that IsValid() fails but no errors were set in
// partial_path.errors?
CHECK(path_is_valid || IsCertStatusError(verify_result->cert_status));
if (!path_is_valid) {
VLOG(1) << "CertVerifyProcBuiltin for " << hostname << " failed:\n"
<< partial_path.errors.ToDebugString(partial_path.certs);
}
const PathBuilderDelegateDataImpl* delegate_data =
PathBuilderDelegateDataImpl::Get(partial_path);
if (delegate_data) {
verify_result->ocsp_result = delegate_data->stapled_ocsp_verify_result;
verify_result->scts = std::move(delegate_data->scts);
verify_result->policy_compliance = delegate_data->ct_policy_compliance;
}
return IsCertStatusError(verify_result->cert_status)
? MapCertStatusToNetError(verify_result->cert_status)
: OK;
}
// Returns true if retrying path building with a less stringent signature
// algorithm *might* successfully build a path, based on the earlier failed
// |result|.
//
// This implementation is simplistic, and looks only for the presence of the
// kUnacceptableSignatureAlgorithm error somewhere among the built paths.
bool CanTryAgainWithWeakerDigestPolicy(
const bssl::CertPathBuilder::Result& result) {
return result.AnyPathContainsError(
bssl::cert_errors::kUnacceptableSignatureAlgorithm);
}
// Returns true if retrying with the system time as the verification time might
// successfully build a path, based on the earlier failed |result|.
bool CanTryAgainWithSystemTime(const bssl::CertPathBuilder::Result& result) {
// TODO(crbug.com/363034686): Retries should also be triggered for CT
// failures.
return result.AnyPathContainsError(
bssl::cert_errors::kValidityFailedNotAfter) ||
result.AnyPathContainsError(
bssl::cert_errors::kValidityFailedNotBefore) ||
result.AnyPathContainsError(bssl::cert_errors::kCertificateRevoked) ||
result.AnyPathContainsError(
bssl::cert_errors::kUnableToCheckRevocation);
}
int CertVerifyProcBuiltin::VerifyInternal(X509Certificate* input_cert,
const std::string& hostname,
const std::string& ocsp_response,
const std::string& sct_list,
int flags,
CertVerifyResult* verify_result,
const NetLogWithSource& net_log) {
base::TimeTicks deadline = base::TimeTicks::Now() + kMaxVerificationTime;
bssl::der::GeneralizedTime der_verification_system_time;
bssl::der::GeneralizedTime der_verification_custom_time;
if (!EncodeTimeAsGeneralizedTime(base::Time::Now(),
&der_verification_system_time)) {
// This shouldn't be possible.
// We don't really have a good error code for this type of error.
verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
return ERR_CERT_AUTHORITY_INVALID;
}
bool custom_time_available = false;
base::Time custom_time;
if (time_tracker_.has_value()) {
custom_time_available = time_tracker_->GetTime(
base::Time::Now(), base::TimeTicks::Now(), &custom_time, nullptr);
if (custom_time_available &&
!EncodeTimeAsGeneralizedTime(custom_time,
&der_verification_custom_time)) {
// This shouldn't be possible, but if it somehow happens, just use system
// time.
custom_time_available = false;
}
}
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
int64_t chrome_root_store_version =
system_trust_store_->chrome_root_store_version();
if (chrome_root_store_version != 0) {
net_log.AddEvent(
NetLogEventType::CERT_VERIFY_PROC_CHROME_ROOT_STORE_VERSION, [&] {
return NetLogChromeRootStoreVersion(chrome_root_store_version);
});
}
#endif
// TODO(crbug.com/40928765): Netlog extra configuration information stored
// inside CertVerifyProcBuiltin (e.g. certs in additional_trust_store and
// system trust store)
// Parse the target certificate.
std::shared_ptr<const bssl::ParsedCertificate> target;
{
bssl::CertErrors parsing_errors;
target =
ParseCertificateFromBuffer(input_cert->cert_buffer(), &parsing_errors);
// TODO(crbug.com/40479281): this duplicates the logging of the input chain
// maybe should only log if there is a parse error/warning?
net_log.AddEvent(NetLogEventType::CERT_VERIFY_PROC_TARGET_CERT, [&] {
return NetLogCertParams(input_cert->cert_buffer(), parsing_errors);
});
if (!target) {
verify_result->cert_status |= CERT_STATUS_INVALID;
return ERR_CERT_INVALID;
}
}
// Parse the provided intermediates.
bssl::CertIssuerSourceStatic intermediates;
AddIntermediatesToIssuerSource(input_cert, &intermediates, net_log);
CertVerifyProcTrustStore trust_store(system_trust_store_.get(),
&additional_trust_store_);
// Get the global dependencies.
const EVRootCAMetadata* ev_metadata = EVRootCAMetadata::GetInstance();
// This boolean tracks whether online revocation checking was performed for
// *any* of the built paths, and not just the final path returned (used for
// setting output flag CERT_STATUS_REV_CHECKING_ENABLED).
bool checked_revocation_for_some_path = false;
// Run path building with the different parameters (attempts) until a valid
// path is found. Earlier successful attempts have priority over later
// attempts.
//
// Attempts are enqueued into |attempts| and drained in FIFO order.
std::vector<BuildPathAttempt> attempts;
// First try EV validation. Can skip this if the leaf certificate has no
// chance of verifying as EV (lacks an EV policy).
if (IsEVCandidate(ev_metadata, target.get()))
attempts.emplace_back(VerificationType::kEV, !custom_time_available);
// Next try DV validation.
attempts.emplace_back(VerificationType::kDV, !custom_time_available);
bssl::CertPathBuilder::Result result;
VerificationType verification_type = VerificationType::kDV;
// Iterate over |attempts| until there are none left to try, or an attempt
// succeeded.
for (size_t cur_attempt_index = 0; cur_attempt_index < attempts.size();
++cur_attempt_index) {
const auto& cur_attempt = attempts[cur_attempt_index];
verification_type = cur_attempt.verification_type;
net_log.BeginEvent(
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT, [&] {
base::Value::Dict results;
if (verification_type == VerificationType::kEV)
results.Set("is_ev_attempt", true);
results.Set("is_network_time_attempt", !cur_attempt.use_system_time);
if (!cur_attempt.use_system_time) {
results.Set(
"network_time_value",
NetLogNumberValue(custom_time.InMillisecondsSinceUnixEpoch()));
}
results.Set("digest_policy",
static_cast<int>(cur_attempt.digest_policy));
return results;
});
// If a previous attempt used up most/all of the deadline, extend the
// deadline a little bit to give this verification attempt a chance at
// success.
deadline = std::max(
deadline, base::TimeTicks::Now() + kPerAttemptMinVerificationTimeLimit);
// Run the attempt through the path builder.
result = TryBuildPath(
target, &intermediates, &trust_store, additional_constraints_,
cur_attempt.use_system_time ? der_verification_system_time
: der_verification_custom_time,
cur_attempt.use_system_time ? base::Time::Now() : custom_time, deadline,
cur_attempt.verification_type, cur_attempt.digest_policy, flags,
ocsp_response, sct_list, crl_set(), ct_verifier_.get(),
ct_policy_enforcer_.get(), net_fetcher_.get(), ev_metadata,
&checked_revocation_for_some_path, net_log);
net_log.EndEvent(NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT,
[&] { return NetLogPathBuilderResult(result); });
if (result.HasValidPath())
break;
if (result.exceeded_deadline) {
// Stop immediately if an attempt exceeds the deadline.
break;
}
if (!cur_attempt.use_system_time && CanTryAgainWithSystemTime(result)) {
BuildPathAttempt system_time_attempt = cur_attempt;
system_time_attempt.use_system_time = true;
attempts.push_back(system_time_attempt);
} else if (cur_attempt.digest_policy ==
bssl::SimplePathBuilderDelegate::DigestPolicy::kStrong &&
CanTryAgainWithWeakerDigestPolicy(result)) {
// If this path building attempt (may have) failed due to the chain using
// a
// weak signature algorithm, enqueue a similar attempt but with weaker
// signature algorithms (SHA1) permitted.
//
// This fallback is necessary because the CertVerifyProc layer may decide
// to allow SHA1 based on its own policy, so path building should return
// possibly weak chains too.
//
// TODO(eroman): Would be better for the SHA1 policy to be part of the
// delegate instead so it can interact with path building.
BuildPathAttempt sha1_fallback_attempt = cur_attempt;
sha1_fallback_attempt.digest_policy =
bssl::SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1;
attempts.push_back(sha1_fallback_attempt);
}
}
// Write the results to |*verify_result|.
int error = AssignVerifyResult(
input_cert, hostname, result, verification_type,
checked_revocation_for_some_path, &trust_store, verify_result);
if (error == OK) {
LogNameNormalizationMetrics(".Builtin", verify_result->verified_cert.get(),
verify_result->is_issued_by_known_root);
}
return error;
}
} // namespace
scoped_refptr<CertVerifyProc> CreateCertVerifyProcBuiltin(
scoped_refptr<CertNetFetcher> net_fetcher,
scoped_refptr<CRLSet> crl_set,
std::unique_ptr<CTVerifier> ct_verifier,
scoped_refptr<CTPolicyEnforcer> ct_policy_enforcer,
std::unique_ptr<SystemTrustStore> system_trust_store,
const CertVerifyProc::InstanceParams& instance_params,
std::optional<network_time::TimeTracker> time_tracker) {
return base::MakeRefCounted<CertVerifyProcBuiltin>(
std::move(net_fetcher), std::move(crl_set), std::move(ct_verifier),
std::move(ct_policy_enforcer), std::move(system_trust_store),
instance_params, std::move(time_tracker));
}
base::TimeDelta GetCertVerifyProcBuiltinTimeLimitForTesting() {
return kMaxVerificationTime;
}
} // namespace net