#include "net/cert/trial_comparison_cert_verifier_util.h"
#include "build/build_config.h"
#include "crypto/sha2.h"
#include "net/base/hash_value.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/pki/cert_errors.h"
#include "net/cert/pki/parsed_certificate.h"
#include "net/cert/x509_util.h"
namespace net {
namespace {
std::shared_ptr<const 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;
std::shared_ptr<const ParsedCertificate> target =
ParsedCertificateFromBuffer(cert->cert_buffer(), &parsing_errors);
if (!target)
return {};
certs.push_back(target);
for (const auto& buf : cert->intermediate_buffers()) {
std::shared_ptr<const ParsedCertificate> intermediate =
ParsedCertificateFromBuffer(buf.get(), &parsing_errors);
if (!intermediate)
return {};
certs.push_back(intermediate);
}
return certs;
}
bool CertHasMultipleEVPoliciesAndOneMatchesRoot(const X509Certificate* cert) {
if (cert->intermediate_buffers().empty())
return false;
ParsedCertificateList certs = ParsedCertificateListFromX509Certificate(cert);
if (certs.empty())
return false;
const ParsedCertificate* leaf = certs.front().get();
const 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->IsEVPolicyOID(oid)) {
candidate_oids.insert(oid);
}
}
if (candidate_oids.size() <= 1)
return false;
SHA256HashValue root_fingerprint;
crypto::SHA256HashString(root->der_cert().AsStringView(),
root_fingerprint.data,
sizeof(root_fingerprint.data));
for (const der::Input& oid : candidate_oids) {
if (ev_metadata->HasEVPolicyOID(root_fingerprint, oid)) {
return true;
}
}
return false;
}
SHA256HashValue GetRootHash(const X509Certificate* cert) {
SHA256HashValue sha256;
if (cert->intermediate_buffers().empty()) {
return sha256;
}
CRYPTO_BUFFER* root = cert->intermediate_buffers().back().get();
return X509Certificate::CalculateFingerprint256(root);
}
const SHA256HashValue lets_encrypt_dst_x3_sha256_fingerprint = {
{0x06, 0x87, 0x26, 0x03, 0x31, 0xA7, 0x24, 0x03, 0xD9, 0x09, 0xF1,
0x05, 0xE6, 0x9B, 0xCF, 0x0D, 0x32, 0xE1, 0xBD, 0x24, 0x93, 0xFF,
0xC6, 0xD9, 0x20, 0x6D, 0x11, 0xBC, 0xD6, 0x77, 0x07, 0x39}};
const SHA256HashValue lets_encrypt_isrg_x1_sha256_fingerprint = {
{0x96, 0xBC, 0xEC, 0x06, 0x26, 0x49, 0x76, 0xF3, 0x74, 0x60, 0x77,
0x9A, 0xCF, 0x28, 0xC5, 0xA7, 0xCF, 0xE8, 0xA3, 0xC0, 0xAA, 0xE1,
0x1A, 0x8F, 0xFC, 0xEE, 0x05, 0xC0, 0xBD, 0xDF, 0x08, 0xC6}};
}
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,
bool sha1_local_anchors_enabled) {
DCHECK(primary_result.verified_cert);
DCHECK(trial_result.verified_cert);
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)) {
if (CertHasMultipleEVPoliciesAndOneMatchesRoot(
trial_result.verified_cert.get())) {
return TrialComparisonResult::kIgnoredMultipleEVPoliciesAndOneMatchesRoot;
}
}
if (!(sha1_local_anchors_enabled &&
(!primary_result.is_issued_by_known_root ||
!trial_result.is_issued_by_known_root)) &&
(primary_result.cert_status & CERT_STATUS_SHA1_SIGNATURE_PRESENT) &&
(trial_result.cert_status & CERT_STATUS_SHA1_SIGNATURE_PRESENT) &&
primary_error != OK && trial_error != OK) {
return TrialComparisonResult::kIgnoredSHA1SignaturePresent;
}
if ((primary_result.cert_status & CERT_STATUS_AUTHORITY_INVALID) &&
(trial_result.cert_status & CERT_STATUS_AUTHORITY_INVALID)) {
return TrialComparisonResult::kIgnoredBothAuthorityInvalid;
}
if (!chains_equal && (primary_error == trial_error) &&
primary_result.is_issued_by_known_root &&
trial_result.is_issued_by_known_root &&
(primary_result.cert_status == trial_result.cert_status)) {
return TrialComparisonResult::kIgnoredBothKnownRoot;
}
if (primary_error != OK && trial_error == ERR_CERT_AUTHORITY_INVALID &&
(primary_result.cert_status & CERT_STATUS_SYMANTEC_LEGACY)) {
return TrialComparisonResult::
kIgnoredBuiltinAuthorityInvalidPlatformSymantec;
}
if (primary_error == ERR_CERT_DATE_INVALID && trial_error == OK &&
(primary_result.cert_status & CERT_STATUS_ALL_ERRORS) ==
CERT_STATUS_DATE_INVALID) {
SHA256HashValue primary_root_hash =
GetRootHash(primary_result.verified_cert.get());
SHA256HashValue trial_root_hash =
GetRootHash(trial_result.verified_cert.get());
if (primary_root_hash == lets_encrypt_dst_x3_sha256_fingerprint &&
trial_root_hash == lets_encrypt_isrg_x1_sha256_fingerprint) {
return TrialComparisonResult::kIgnoredLetsEncryptExpiredRoot;
}
}
#if BUILDFLAG(IS_ANDROID)
if (primary_error == ERR_CERT_DATE_INVALID &&
trial_error == ERR_CERT_AUTHORITY_INVALID &&
(primary_result.cert_status & CERT_STATUS_ALL_ERRORS) ==
CERT_STATUS_DATE_INVALID &&
(trial_result.cert_status & CERT_STATUS_ALL_ERRORS) ==
CERT_STATUS_AUTHORITY_INVALID) {
return TrialComparisonResult::kIgnoredAndroidErrorDatePriority;
}
#endif
return TrialComparisonResult::kInvalid;
}
}