0

Refactor trial cert comparison verifier to extract logic for comparing

results; reuse that logic in cert_verify_comparision_tool.

Bug: 1248209
Change-Id: Ic99a5c4eaf66753fc7effffbf80ee6d523e798a4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3243523
Commit-Queue: Hubert Chao <hchao@chromium.org>
Reviewed-by: Matt Mueller <mattm@chromium.org>
Cr-Commit-Position: refs/heads/main@{#936138}
This commit is contained in:
Hubert Chao
2021-10-28 22:38:27 +00:00
committed by Chromium LUCI CQ
parent 7d27c1ec05
commit 99aa7f45b6
7 changed files with 263 additions and 199 deletions

@ -379,6 +379,8 @@ component("net") {
"cert/symantec_certs.h",
"cert/test_root_certs.cc",
"cert/test_root_certs.h",
"cert/trial_comparison_cert_verifier_util.cc",
"cert/trial_comparison_cert_verifier_util.h",
"cert/x509_cert_types.cc",
"cert/x509_cert_types.h",
"cert/x509_certificate.cc",

@ -13,14 +13,11 @@
#include "base/task/post_task.h"
#include "base/values.h"
#include "build/build_config.h"
#include "crypto/sha2.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_verify_proc.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/ev_root_ca_metadata.h"
#include "net/cert/internal/cert_errors.h"
#include "net/cert/internal/parsed_certificate.h"
#include "net/cert/multi_threaded_cert_verifier.h"
#include "net/cert/trial_comparison_cert_verifier_util.h"
#include "net/cert/x509_util.h"
#include "net/log/net_log.h"
#include "net/log/net_log_event_type.h"
@ -37,91 +34,6 @@ base::Value JobResultParams(bool trial_success) {
return results;
}
// Note: This ignores the result of stapled OCSP (which is the same for both
// verifiers) and informational statuses about the certificate algorithms and
// the hashes, since they will be the same if the certificate chains are the
// same.
bool CertVerifyResultEqual(const CertVerifyResult& a,
const CertVerifyResult& b) {
return std::tie(a.cert_status, a.is_issued_by_known_root) ==
std::tie(b.cert_status, b.is_issued_by_known_root) &&
(!!a.verified_cert == !!b.verified_cert) &&
(!a.verified_cert ||
a.verified_cert->EqualsIncludingChain(b.verified_cert.get()));
}
scoped_refptr<ParsedCertificate> ParsedCertificateFromBuffer(
CRYPTO_BUFFER* cert_handle,
CertErrors* errors) {
return ParsedCertificate::Create(bssl::UpRef(cert_handle),
x509_util::DefaultParseCertificateOptions(),
errors);
}
ParsedCertificateList ParsedCertificateListFromX509Certificate(
const X509Certificate* cert) {
CertErrors parsing_errors;
ParsedCertificateList certs;
scoped_refptr<ParsedCertificate> target =
ParsedCertificateFromBuffer(cert->cert_buffer(), &parsing_errors);
if (!target)
return {};
certs.push_back(target);
for (const auto& buf : cert->intermediate_buffers()) {
scoped_refptr<ParsedCertificate> intermediate =
ParsedCertificateFromBuffer(buf.get(), &parsing_errors);
if (!intermediate)
return {};
certs.push_back(intermediate);
}
return certs;
}
// Tests whether cert has multiple EV policies, and at least one matches the
// root. This is not a complete test of EV, but just enough to give a possible
// explanation as to why the platform verifier did not validate as EV while
// builtin did. (Since only the builtin verifier correctly handles multiple
// candidate EV policies.)
bool CertHasMultipleEVPoliciesAndOneMatchesRoot(const X509Certificate* cert) {
if (cert->intermediate_buffers().empty())
return false;
ParsedCertificateList certs = ParsedCertificateListFromX509Certificate(cert);
if (certs.empty())
return false;
ParsedCertificate* leaf = certs.front().get();
ParsedCertificate* root = certs.back().get();
if (!leaf->has_policy_oids())
return false;
const EVRootCAMetadata* ev_metadata = EVRootCAMetadata::GetInstance();
std::set<der::Input> candidate_oids;
for (const der::Input& oid : leaf->policy_oids()) {
if (ev_metadata->IsEVPolicyOIDGivenBytes(oid))
candidate_oids.insert(oid);
}
if (candidate_oids.size() <= 1)
return false;
SHA256HashValue root_fingerprint;
crypto::SHA256HashString(root->der_cert().AsStringPiece(),
root_fingerprint.data,
sizeof(root_fingerprint.data));
for (const der::Input& oid : candidate_oids) {
if (ev_metadata->HasEVPolicyOIDGivenBytes(root_fingerprint, oid))
return true;
}
return false;
}
} // namespace
// The Job represents the state machine for a trial cert verification.
@ -190,16 +102,6 @@ class TrialComparisonCertVerifier::Job {
// that re-verification completes.
void OnPrimaryReverifyWithSecondaryChainCompleted(int result);
// Check if the differences between the primary and trial verifiers can be
// ignored. This only handles differences that can be checked synchronously.
// If the difference is ignorable, returns the relevant TrialComparisonResult,
// otherwise returns kInvalid.
TrialComparisonResult IsSynchronouslyIgnorableDifference(
int primary_error,
const CertVerifyResult& primary_result,
int trial_error,
const CertVerifyResult& trial_result);
const CertVerifier::Config config_;
bool config_changed_ = false;
const CertVerifier::RequestParams params_;
@ -386,16 +288,16 @@ void TrialComparisonCertVerifier::Job::FinishWithError() {
DCHECK(trial_error_ != primary_error_ ||
!CertVerifyResultEqual(trial_result_, primary_result_));
TrialComparisonResult result_code = kInvalid;
TrialComparisonResult result_code = TrialComparisonResult::kInvalid;
if (primary_error_ == OK && trial_error_ == OK) {
result_code = kBothValidDifferentDetails;
result_code = TrialComparisonResult::kBothValidDifferentDetails;
} else if (primary_error_ == OK) {
result_code = kPrimaryValidSecondaryError;
result_code = TrialComparisonResult::kPrimaryValidSecondaryError;
} else if (trial_error_ == OK) {
result_code = kPrimaryErrorSecondaryValid;
result_code = TrialComparisonResult::kPrimaryErrorSecondaryValid;
} else {
result_code = kBothErrorDifferentDetails;
result_code = TrialComparisonResult::kBothErrorDifferentDetails;
}
Finish(/*is_success=*/false, result_code);
}
@ -461,7 +363,7 @@ void TrialComparisonCertVerifier::Job::OnTrialJobCompleted(int result) {
if (trial_success) {
// Note: Will delete |this|.
FinishSuccess(kEqual);
FinishSuccess(TrialComparisonResult::kEqual);
return;
}
@ -472,7 +374,7 @@ void TrialComparisonCertVerifier::Job::OnTrialJobCompleted(int result) {
(CERT_STATUS_REVOKED | CERT_STATUS_REV_CHECKING_ENABLED))) {
if (config_changed_) {
// Note: Will delete |this|.
FinishSuccess(kIgnoredConfigurationChanged);
FinishSuccess(TrialComparisonResult::kIgnoredConfigurationChanged);
return;
}
@ -498,7 +400,7 @@ void TrialComparisonCertVerifier::Job::OnTrialJobCompleted(int result) {
if (!chains_equal && (trial_error_ == OK || primary_error_ != OK)) {
if (config_changed_) {
// Note: Will delete |this|.
FinishSuccess(kIgnoredConfigurationChanged);
FinishSuccess(TrialComparisonResult::kIgnoredConfigurationChanged);
return;
}
@ -523,7 +425,7 @@ void TrialComparisonCertVerifier::Job::OnTrialJobCompleted(int result) {
TrialComparisonResult ignorable_difference =
IsSynchronouslyIgnorableDifference(primary_error_, primary_result_,
trial_error_, trial_result_);
if (ignorable_difference != kInvalid) {
if (ignorable_difference != TrialComparisonResult::kInvalid) {
FinishSuccess(ignorable_difference); // Note: Will delete |this|.
return;
}
@ -536,7 +438,8 @@ void TrialComparisonCertVerifier::Job::
OnMacRevCheckingReverificationJobCompleted(int result) {
if (result == ERR_CERT_REVOKED) {
// Will delete |this|.
FinishSuccess(kIgnoredMacUndesiredRevocationChecking);
FinishSuccess(
TrialComparisonResult::kIgnoredMacUndesiredRevocationChecking);
return;
}
FinishWithError(); // Note: Will delete |this|.
@ -552,20 +455,22 @@ void TrialComparisonCertVerifier::Job::
// Ignore the difference.
//
// Note: Will delete |this|.
FinishSuccess(kIgnoredDifferentPathReVerifiesEquivalent);
FinishSuccess(
TrialComparisonResult::kIgnoredDifferentPathReVerifiesEquivalent);
return;
}
if (IsSynchronouslyIgnorableDifference(result, reverification_result_,
trial_error_,
trial_result_) != kInvalid) {
trial_error_, trial_result_) !=
TrialComparisonResult::kInvalid) {
// The new result matches if ignoring differences. Still use the
// |kIgnoredDifferentPathReVerifiesEquivalent| code rather than the result
// of IsSynchronouslyIgnorableDifference, since it's the higher level
// description of what the difference is in this case.
//
// Note: Will delete |this|.
FinishSuccess(kIgnoredDifferentPathReVerifiesEquivalent);
FinishSuccess(
TrialComparisonResult::kIgnoredDifferentPathReVerifiesEquivalent);
return;
}
@ -573,39 +478,6 @@ void TrialComparisonCertVerifier::Job::
FinishWithError();
}
TrialComparisonCertVerifier::TrialComparisonResult
TrialComparisonCertVerifier::Job::IsSynchronouslyIgnorableDifference(
int primary_error,
const CertVerifyResult& primary_result,
int trial_error,
const CertVerifyResult& trial_result) {
DCHECK(primary_result.verified_cert);
DCHECK(trial_result.verified_cert);
if (primary_error == OK &&
primary_result.verified_cert->intermediate_buffers().empty()) {
// Platform may support trusting a leaf certificate directly. Builtin
// verifier does not. See https://crbug.com/814994.
return kIgnoredLocallyTrustedLeaf;
}
const bool chains_equal = primary_result.verified_cert->EqualsIncludingChain(
trial_result.verified_cert.get());
if (chains_equal && (trial_result.cert_status & CERT_STATUS_IS_EV) &&
!(primary_result.cert_status & CERT_STATUS_IS_EV) &&
(primary_error == trial_error)) {
// The platform CertVerifyProc impls only check a single potential EV
// policy from the leaf. If the leaf had multiple policies, builtin
// verifier may verify it as EV when the platform verifier did not.
if (CertHasMultipleEVPoliciesAndOneMatchesRoot(
trial_result.verified_cert.get())) {
return kIgnoredMultipleEVPoliciesAndOneMatchesRoot;
}
}
return kInvalid;
}
TrialComparisonCertVerifier::Job::Request::Request(
TrialComparisonCertVerifier::Job* parent,
CertVerifyResult* client_result,

@ -28,23 +28,6 @@ class CertVerifyProc;
// examine the differences.
class NET_EXPORT TrialComparisonCertVerifier : public CertVerifier {
public:
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum TrialComparisonResult {
kInvalid = 0,
kEqual = 1,
kPrimaryValidSecondaryError = 2,
kPrimaryErrorSecondaryValid = 3,
kBothValidDifferentDetails = 4,
kBothErrorDifferentDetails = 5,
kIgnoredMacUndesiredRevocationChecking = 6,
kIgnoredMultipleEVPoliciesAndOneMatchesRoot = 7,
kIgnoredDifferentPathReVerifiesEquivalent = 8,
kIgnoredLocallyTrustedLeaf = 9,
kIgnoredConfigurationChanged = 10,
kMaxValue = kIgnoredConfigurationChanged
};
using ReportCallback = base::RepeatingCallback<void(
const std::string& hostname,
const scoped_refptr<X509Certificate>& unverified_cert,

@ -18,6 +18,7 @@
#include "net/cert/cert_verify_proc.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/ev_root_ca_metadata.h"
#include "net/cert/trial_comparison_cert_verifier_util.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/log/net_log_with_source.h"
@ -421,7 +422,7 @@ TEST_F(TrialComparisonCertVerifierTest, InitiallyDisallowedThenAllowed) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
TrialComparisonResult::kPrimaryValidSecondaryError, 1);
// Expect a report from the second verification.
ASSERT_EQ(1U, reports.size());
@ -505,7 +506,7 @@ TEST_F(TrialComparisonCertVerifierTest, InitiallyAllowedThenDisallowed) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
TrialComparisonResult::kPrimaryValidSecondaryError, 1);
// Expect a report from the first verification.
ASSERT_EQ(1U, reports.size());
@ -608,7 +609,7 @@ TEST_F(TrialComparisonCertVerifierTest, ConfigChangedDuringTrialVerification) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
TrialComparisonResult::kPrimaryValidSecondaryError, 1);
// Expect a report.
ASSERT_EQ(1U, reports.size());
@ -658,7 +659,7 @@ TEST_F(TrialComparisonCertVerifierTest, SameResult) {
histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
1);
histograms_.ExpectUniqueSample("Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kEqual, 1);
TrialComparisonResult::kEqual, 1);
}
TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierErrorSecondaryOk) {
@ -723,7 +724,7 @@ TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierErrorSecondaryOk) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
TrialComparisonResult::kPrimaryErrorSecondaryValid, 1);
}
TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierOkSecondaryError) {
@ -784,7 +785,7 @@ TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierOkSecondaryError) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
TrialComparisonResult::kPrimaryValidSecondaryError, 1);
}
TEST_F(TrialComparisonCertVerifierTest, BothVerifiersDifferentErrors) {
@ -847,7 +848,7 @@ TEST_F(TrialComparisonCertVerifierTest, BothVerifiersDifferentErrors) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kBothErrorDifferentDetails, 1);
TrialComparisonResult::kBothErrorDifferentDetails, 1);
}
TEST_F(TrialComparisonCertVerifierTest,
@ -909,7 +910,7 @@ TEST_F(TrialComparisonCertVerifierTest,
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
TrialComparisonResult::kBothValidDifferentDetails, 1);
}
TEST_F(TrialComparisonCertVerifierTest,
@ -969,7 +970,7 @@ TEST_F(TrialComparisonCertVerifierTest,
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kIgnoredConfigurationChanged, 1);
TrialComparisonResult::kIgnoredConfigurationChanged, 1);
}
TEST_F(TrialComparisonCertVerifierTest,
@ -1027,8 +1028,7 @@ TEST_F(TrialComparisonCertVerifierTest,
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kIgnoredDifferentPathReVerifiesEquivalent,
1);
TrialComparisonResult::kIgnoredDifferentPathReVerifiesEquivalent, 1);
}
TEST_F(TrialComparisonCertVerifierTest,
@ -1130,8 +1130,7 @@ TEST_F(TrialComparisonCertVerifierTest,
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kIgnoredDifferentPathReVerifiesEquivalent,
1);
TrialComparisonResult::kIgnoredDifferentPathReVerifiesEquivalent, 1);
}
TEST_F(TrialComparisonCertVerifierTest, BothVerifiersOkDifferentCertStatus) {
@ -1202,7 +1201,7 @@ TEST_F(TrialComparisonCertVerifierTest, BothVerifiersOkDifferentCertStatus) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
TrialComparisonResult::kBothValidDifferentDetails, 1);
}
TEST_F(TrialComparisonCertVerifierTest, CancelledDuringPrimaryVerification) {
@ -1267,7 +1266,7 @@ TEST_F(TrialComparisonCertVerifierTest, CancelledDuringPrimaryVerification) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
TrialComparisonResult::kPrimaryErrorSecondaryValid, 1);
}
TEST_F(TrialComparisonCertVerifierTest, DeletedDuringPrimaryVerification) {
@ -1436,7 +1435,7 @@ TEST_F(TrialComparisonCertVerifierTest, DeletedDuringTrialReport) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
TrialComparisonResult::kPrimaryErrorSecondaryValid, 1);
}
TEST_F(TrialComparisonCertVerifierTest, DeletedAfterTrialVerificationStarted) {
@ -1562,14 +1561,14 @@ TEST_F(TrialComparisonCertVerifierTest, MacUndesiredRevocationChecking) {
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kIgnoredMacUndesiredRevocationChecking, 1);
TrialComparisonResult::kIgnoredMacUndesiredRevocationChecking, 1);
#else
// Expect a report.
EXPECT_EQ(1U, reports.size());
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
TrialComparisonResult::kPrimaryErrorSecondaryValid, 1);
#endif
}
@ -1629,7 +1628,7 @@ TEST_F(TrialComparisonCertVerifierTest, PrimaryRevokedSecondaryOk) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
TrialComparisonResult::kPrimaryErrorSecondaryValid, 1);
// Expect a report.
EXPECT_EQ(1U, reports.size());
@ -1704,8 +1703,7 @@ TEST_F(TrialComparisonCertVerifierTest, MultipleEVPolicies) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kIgnoredMultipleEVPoliciesAndOneMatchesRoot,
1);
TrialComparisonResult::kIgnoredMultipleEVPoliciesAndOneMatchesRoot, 1);
}
TEST_F(TrialComparisonCertVerifierTest, MultipleEVPoliciesNoneValidForRoot) {
@ -1770,7 +1768,7 @@ TEST_F(TrialComparisonCertVerifierTest, MultipleEVPoliciesNoneValidForRoot) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
TrialComparisonResult::kBothValidDifferentDetails, 1);
}
TEST_F(TrialComparisonCertVerifierTest, MultiplePoliciesOnlyOneIsEV) {
@ -1839,7 +1837,7 @@ TEST_F(TrialComparisonCertVerifierTest, MultiplePoliciesOnlyOneIsEV) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
TrialComparisonResult::kBothValidDifferentDetails, 1);
}
TEST_F(TrialComparisonCertVerifierTest, LocallyTrustedLeaf) {
@ -1890,7 +1888,7 @@ TEST_F(TrialComparisonCertVerifierTest, LocallyTrustedLeaf) {
1);
histograms_.ExpectUniqueSample(
"Net.CertVerifier_TrialComparisonResult",
TrialComparisonCertVerifier::kIgnoredLocallyTrustedLeaf, 1);
TrialComparisonResult::kIgnoredLocallyTrustedLeaf, 1);
}
} // namespace net

@ -0,0 +1,138 @@
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/cert/trial_comparison_cert_verifier_util.h"
#include "crypto/sha2.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/ev_root_ca_metadata.h"
#include "net/cert/internal/cert_errors.h"
#include "net/cert/internal/parsed_certificate.h"
#include "net/cert/x509_util.h"
namespace net {
namespace {
scoped_refptr<ParsedCertificate> ParsedCertificateFromBuffer(
CRYPTO_BUFFER* cert_handle,
CertErrors* errors) {
return ParsedCertificate::Create(bssl::UpRef(cert_handle),
x509_util::DefaultParseCertificateOptions(),
errors);
}
ParsedCertificateList ParsedCertificateListFromX509Certificate(
const X509Certificate* cert) {
CertErrors parsing_errors;
ParsedCertificateList certs;
scoped_refptr<ParsedCertificate> target =
ParsedCertificateFromBuffer(cert->cert_buffer(), &parsing_errors);
if (!target)
return {};
certs.push_back(target);
for (const auto& buf : cert->intermediate_buffers()) {
scoped_refptr<ParsedCertificate> intermediate =
ParsedCertificateFromBuffer(buf.get(), &parsing_errors);
if (!intermediate)
return {};
certs.push_back(intermediate);
}
return certs;
}
// Tests whether cert has multiple EV policies, and at least one matches the
// root. This is not a complete test of EV, but just enough to give a possible
// explanation as to why the platform verifier did not validate as EV while
// builtin did. (Since only the builtin verifier correctly handles multiple
// candidate EV policies.)
bool CertHasMultipleEVPoliciesAndOneMatchesRoot(const X509Certificate* cert) {
if (cert->intermediate_buffers().empty())
return false;
ParsedCertificateList certs = ParsedCertificateListFromX509Certificate(cert);
if (certs.empty())
return false;
ParsedCertificate* leaf = certs.front().get();
ParsedCertificate* root = certs.back().get();
if (!leaf->has_policy_oids())
return false;
const EVRootCAMetadata* ev_metadata = EVRootCAMetadata::GetInstance();
std::set<der::Input> candidate_oids;
for (const der::Input& oid : leaf->policy_oids()) {
if (ev_metadata->IsEVPolicyOIDGivenBytes(oid))
candidate_oids.insert(oid);
}
if (candidate_oids.size() <= 1)
return false;
SHA256HashValue root_fingerprint;
crypto::SHA256HashString(root->der_cert().AsStringPiece(),
root_fingerprint.data,
sizeof(root_fingerprint.data));
for (const der::Input& oid : candidate_oids) {
if (ev_metadata->HasEVPolicyOIDGivenBytes(root_fingerprint, oid))
return true;
}
return false;
}
} // namespace
// Note: This ignores the result of stapled OCSP (which is the same for both
// verifiers) and informational statuses about the certificate algorithms and
// the hashes, since they will be the same if the certificate chains are the
// same.
bool CertVerifyResultEqual(const CertVerifyResult& a,
const CertVerifyResult& b) {
return std::tie(a.cert_status, a.is_issued_by_known_root) ==
std::tie(b.cert_status, b.is_issued_by_known_root) &&
(!!a.verified_cert == !!b.verified_cert) &&
(!a.verified_cert ||
a.verified_cert->EqualsIncludingChain(b.verified_cert.get()));
}
TrialComparisonResult IsSynchronouslyIgnorableDifference(
int primary_error,
const CertVerifyResult& primary_result,
int trial_error,
const CertVerifyResult& trial_result) {
DCHECK(primary_result.verified_cert);
DCHECK(trial_result.verified_cert);
if (primary_error == OK &&
primary_result.verified_cert->intermediate_buffers().empty()) {
// Platform may support trusting a leaf certificate directly. Builtin
// verifier does not. See https://crbug.com/814994.
return TrialComparisonResult::kIgnoredLocallyTrustedLeaf;
}
const bool chains_equal = primary_result.verified_cert->EqualsIncludingChain(
trial_result.verified_cert.get());
if (chains_equal && (trial_result.cert_status & CERT_STATUS_IS_EV) &&
!(primary_result.cert_status & CERT_STATUS_IS_EV) &&
(primary_error == trial_error)) {
// The platform CertVerifyProc impls only check a single potential EV
// policy from the leaf. If the leaf had multiple policies, builtin
// verifier may verify it as EV when the platform verifier did not.
if (CertHasMultipleEVPoliciesAndOneMatchesRoot(
trial_result.verified_cert.get())) {
return TrialComparisonResult::kIgnoredMultipleEVPoliciesAndOneMatchesRoot;
}
}
return TrialComparisonResult::kInvalid;
}
} // namespace net

@ -0,0 +1,41 @@
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_CERT_TRIAL_COMPARISON_CERT_VERIFIER_UTIL_H_
#define NET_CERT_TRIAL_COMPARISON_CERT_VERIFIER_UTIL_H_
#include "net/base/net_export.h"
#include "net/cert/cert_verify_result.h"
namespace net {
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class TrialComparisonResult {
kInvalid = 0,
kEqual = 1,
kPrimaryValidSecondaryError = 2,
kPrimaryErrorSecondaryValid = 3,
kBothValidDifferentDetails = 4,
kBothErrorDifferentDetails = 5,
kIgnoredMacUndesiredRevocationChecking = 6,
kIgnoredMultipleEVPoliciesAndOneMatchesRoot = 7,
kIgnoredDifferentPathReVerifiesEquivalent = 8,
kIgnoredLocallyTrustedLeaf = 9,
kIgnoredConfigurationChanged = 10,
kMaxValue = kIgnoredConfigurationChanged
};
NET_EXPORT_PRIVATE bool CertVerifyResultEqual(const CertVerifyResult& a,
const CertVerifyResult& b);
NET_EXPORT_PRIVATE TrialComparisonResult
IsSynchronouslyIgnorableDifference(int primary_error,
const CertVerifyResult& primary_result,
int trial_error,
const CertVerifyResult& trial_result);
} // namespace net
#endif // NET_CERT_TRIAL_COMPARISON_CERT_VERIFIER_UTIL_H_

@ -25,6 +25,7 @@
#include "net/cert/cert_verify_proc_builtin.h"
#include "net/cert/crl_set.h"
#include "net/cert/internal/system_trust_store.h"
#include "net/cert/trial_comparison_cert_verifier_util.h"
#include "net/cert/x509_certificate.h"
#include "net/cert_net/cert_net_fetcher_url_request.h"
#include "net/tools/cert_verify_tool/cert_verify_tool_util.h"
@ -142,15 +143,6 @@ std::unique_ptr<CertVerifyImpl> CreateCertVerifyImplFromName(
return nullptr;
}
bool CertVerifyResultEqual(const net::CertVerifyResult& a,
const net::CertVerifyResult& b) {
return std::tie(a.cert_status, a.is_issued_by_known_root) ==
std::tie(b.cert_status, b.is_issued_by_known_root) &&
(!!a.verified_cert == !!b.verified_cert) &&
(!a.verified_cert ||
a.verified_cert->EqualsIncludingChain(b.verified_cert.get()));
}
const char kUsage[] =
" --input=<file>\n"
"\n"
@ -225,20 +217,58 @@ int RunCert(base::File* input_file,
net::CertVerifyResult builtin_result;
int builtin_error;
// TODO(mattm,hchao): Explore calling TrialComparisonCertVerifier directly
// to have the extra comparison logic built into both places directly.
platform_proc->VerifyCert(*x509_target_and_intermediates, cert_chain.host(),
&platform_result, &platform_error);
builtin_proc->VerifyCert(*x509_target_and_intermediates, cert_chain.host(),
&builtin_result, &builtin_error);
if (CertVerifyResultEqual(platform_result, builtin_result) &&
if (net::CertVerifyResultEqual(platform_result, builtin_result) &&
platform_error == builtin_error) {
std::cerr << "Host " << cert_chain.host() << " has equal verify results!\n";
} else {
// Much of the below code is lifted from
// TrialComparisonCertVerifier::Job::OnTrialJobCompleted as it wasn't
// obvious how to easily refactor the code here to prevent copying this
// section of code.
const bool chains_equal =
platform_result.verified_cert->EqualsIncludingChain(
builtin_result.verified_cert.get());
// Chains built were different with either builtin being OK or both not OK.
// Pass builtin chain to platform, see if platform comes back the same.
if (!chains_equal &&
(builtin_error == net::OK || platform_error != net::OK)) {
net::CertVerifyResult platform_reverification_result;
int platform_reverification_error;
platform_proc->VerifyCert(
*builtin_result.verified_cert, cert_chain.host(),
&platform_reverification_result, &platform_reverification_error);
if (net::CertVerifyResultEqual(platform_reverification_result,
builtin_result) &&
platform_reverification_error == builtin_error) {
std::cerr << "Host " << cert_chain.host()
<< " has equal reverify results!\n";
return 0;
}
}
net::TrialComparisonResult result = net::IsSynchronouslyIgnorableDifference(
platform_error, platform_result, builtin_error, builtin_result);
// TODO(hchao): gather stats on result values.
if (result != net::TrialComparisonResult::kInvalid) {
std::cerr << "Host " << cert_chain.host()
<< " has ignorable verify results!";
return 0;
}
std::cerr << "Host " << cert_chain.host()
<< " has different verify results!\n";
// TODO(hchao): print input chain.
std::cerr << "Platform: (error = "
<< net::ErrorToShortString(platform_error) << ")\n";
PrintCertVerifyResult(platform_result);