Chromium Code Reviews| Index: net/cert/cert_policy_enforcer.cc |
| diff --git a/net/cert/cert_policy_enforcer.cc b/net/cert/cert_policy_enforcer.cc |
| index 0d05366a1162a746751c58ea98c71f22c6193a3c..9480283a212a52af724e3d025c30eda3213d4bff 100644 |
| --- a/net/cert/cert_policy_enforcer.cc |
| +++ b/net/cert/cert_policy_enforcer.cc |
| @@ -6,15 +6,20 @@ |
| #include <algorithm> |
| +#include "base/bind.h" |
| #include "base/build_time.h" |
| +#include "base/callback_helpers.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/histogram.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/strings/string_number_conversions.h" |
| +#include "base/values.h" |
| +#include "net/base/net_log.h" |
| #include "net/cert/ct_ev_whitelist.h" |
| #include "net/cert/ct_verify_result.h" |
| #include "net/cert/signed_certificate_timestamp.h" |
| #include "net/cert/x509_certificate.h" |
| +#include "net/cert/x509_certificate_net_log_param.h" |
| namespace net { |
| @@ -53,6 +58,48 @@ uint32_t ApproximateMonthDifference(const base::Time& start, |
| return month_diff; |
| } |
| +bool HasRequiredNumberOfSCTs(X509Certificate* cert, |
|
mmenke
2014/12/16 16:40:12
optional nit: This should probably be const X509C
Eran Messeri
2014/12/17 16:19:31
Done, here and in IsCertificateInWhitelist
|
| + const ct::CTVerifyResult& ct_result) { |
| + // TODO(eranm): Count the number of *independent* SCTs once the information |
| + // about log operators is available, crbug.com/425174 |
| + size_t num_valid_scts = ct_result.verified_scts.size(); |
| + size_t num_embedded_scts = |
| + std::count_if(ct_result.verified_scts.begin(), |
| + ct_result.verified_scts.end(), IsEmbeddedSCT); |
| + |
| + size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts; |
| + // If at least two valid SCTs were delivered by means other than embedding |
| + // (i.e. in a TLS extension or OCSP), then the certificate conforms to bullet |
| + // number 3 of the "Qualifying Certificate" section of the CT/EV policy. |
| + if (num_non_embedded_scts >= 2) |
| + return true; |
| + |
| + if (cert->valid_start().is_null() || cert->valid_expiry().is_null() || |
| + cert->valid_start().is_max() || cert->valid_expiry().is_max()) { |
| + // Will not be able to calculate the certificate's validity period. |
| + return false; |
| + } |
| + |
| + uint32_t expiry_in_months_approx = |
| + ApproximateMonthDifference(cert->valid_start(), cert->valid_expiry()); |
| + |
| + // For embedded SCTs, if the certificate has the number of SCTs specified in |
| + // table 1 of the "Qualifying Certificate" section of the CT/EV policy, then |
| + // it qualifies. |
| + size_t num_required_embedded_scts; |
| + if (expiry_in_months_approx > 39) { |
| + num_required_embedded_scts = 5; |
| + } else if (expiry_in_months_approx > 27) { |
| + num_required_embedded_scts = 4; |
| + } else if (expiry_in_months_approx >= 15) { |
| + num_required_embedded_scts = 3; |
| + } else { |
| + num_required_embedded_scts = 2; |
| + } |
| + |
| + return num_embedded_scts >= num_required_embedded_scts; |
| +} |
| + |
| enum CTComplianceStatus { |
| CT_NOT_COMPLIANT = 0, |
| CT_IN_WHITELIST = 1, |
| @@ -60,103 +107,148 @@ enum CTComplianceStatus { |
| CT_COMPLIANCE_MAX, |
| }; |
| +const char* ComplianceStatusToString(CTComplianceStatus status) { |
| + switch (status) { |
| + case CT_NOT_COMPLIANT: |
| + return "NOT_COMPLIANT"; |
| + break; |
| + case CT_IN_WHITELIST: |
| + return "WHITELISTED"; |
| + break; |
| + case CT_ENOUGH_SCTS: |
| + return "ENOUGH_SCTS"; |
| + break; |
| + case CT_COMPLIANCE_MAX: |
| + break; |
| + } |
| + |
| + return "unknown"; |
| +} |
| + |
| void LogCTComplianceStatusToUMA(CTComplianceStatus status) { |
| UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status, |
| CT_COMPLIANCE_MAX); |
| } |
| -} // namespace |
| +struct ComplianceDetails { |
| + ComplianceDetails() |
| + : ct_presence_required(false), |
| + build_timely(false), |
| + status(CT_NOT_COMPLIANT) {} |
| + |
| + // Whether enforcement of the policy was required or not. |
| + bool ct_presence_required; |
| + // Whether the build is not older than 10 weeks. The value is meaningful only |
| + // if |ct_presence_required| is true. |
| + bool build_timely; |
| + // Compliance status - meaningful only if |ct_presence_required| and |
| + // |build_timely| are true. |
| + CTComplianceStatus status; |
| + // EV whitelist version. |
| + base::Version whitelist_version; |
| +}; |
| -CertPolicyEnforcer::CertPolicyEnforcer(bool require_ct_for_ev) |
| - : require_ct_for_ev_(require_ct_for_ev) { |
| +base::Value* NetLogComplianceCheckResultCallback(X509Certificate* cert, |
| + ComplianceDetails* details, |
| + NetLog::LogLevel log_level) { |
| + base::DictionaryValue* dict = new base::DictionaryValue(); |
| + dict->Set("certificate", NetLogX509CertificateCallback(cert, log_level)); |
| + dict->SetBoolean("policy_enforcement_required", |
| + details->ct_presence_required); |
| + if (details->ct_presence_required) { |
| + dict->SetBoolean("build_timely", details->build_timely); |
| + if (details->build_timely) { |
| + dict->SetString("ct_compliance_status", |
| + ComplianceStatusToString(details->status)); |
| + if (details->whitelist_version.IsValid()) |
| + dict->SetString("ev_whitelist_version", |
| + details->whitelist_version.GetString()); |
| + } |
| + } |
| + return dict; |
| } |
| -CertPolicyEnforcer::~CertPolicyEnforcer() { |
| +bool IsCertificateInWhitelist(X509Certificate* cert, |
| + const ct::EVCertsWhitelist* ev_whitelist) { |
| + bool cert_in_ev_whitelist = false; |
| + if (ev_whitelist && ev_whitelist->IsValid()) { |
| + const SHA256HashValue fingerprint( |
| + X509Certificate::CalculateFingerprint256(cert->os_cert_handle())); |
| + |
| + std::string truncated_fp = |
| + std::string(reinterpret_cast<const char*>(fingerprint.data), 8); |
| + cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp); |
| + |
| + UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist", |
| + cert_in_ev_whitelist); |
| + } |
| + return cert_in_ev_whitelist; |
| } |
| -bool CertPolicyEnforcer::DoesConformToCTEVPolicy( |
| - X509Certificate* cert, |
| - const ct::EVCertsWhitelist* ev_whitelist, |
| - const ct::CTVerifyResult& ct_result) { |
| - if (!require_ct_for_ev_) |
| - return true; |
| +void CheckCTEVPolicyCompliance(X509Certificate* cert, |
| + const ct::EVCertsWhitelist* ev_whitelist, |
| + const ct::CTVerifyResult& ct_result, |
| + ComplianceDetails* result) { |
| + result->ct_presence_required = true; |
| if (!IsBuildTimely()) |
| - return false; |
| + return; |
| + result->build_timely = true; |
| + |
| + if (ev_whitelist && ev_whitelist->IsValid()) |
| + result->whitelist_version = ev_whitelist->Version(); |
| if (IsCertificateInWhitelist(cert, ev_whitelist)) { |
| - LogCTComplianceStatusToUMA(CT_IN_WHITELIST); |
| - return true; |
| + result->status = CT_IN_WHITELIST; |
| + return; |
| } |
| if (HasRequiredNumberOfSCTs(cert, ct_result)) { |
| - LogCTComplianceStatusToUMA(CT_ENOUGH_SCTS); |
| - return true; |
| + result->status = CT_ENOUGH_SCTS; |
| + return; |
| } |
| - LogCTComplianceStatusToUMA(CT_NOT_COMPLIANT); |
| - return false; |
| + result->status = CT_NOT_COMPLIANT; |
| } |
| -bool CertPolicyEnforcer::IsCertificateInWhitelist( |
| - X509Certificate* cert, |
| - const ct::EVCertsWhitelist* ev_whitelist) { |
| - bool cert_in_ev_whitelist = false; |
| - if (ev_whitelist && ev_whitelist->IsValid()) { |
| - const SHA256HashValue fingerprint( |
| - X509Certificate::CalculateFingerprint256(cert->os_cert_handle())); |
| +} // namespace |
| - std::string truncated_fp = |
| - std::string(reinterpret_cast<const char*>(fingerprint.data), 8); |
| - cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp); |
| +CertPolicyEnforcer::CertPolicyEnforcer(bool require_ct_for_ev) |
| + : require_ct_for_ev_(require_ct_for_ev) { |
| +} |
| - UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist", |
| - cert_in_ev_whitelist); |
| - } |
| - return cert_in_ev_whitelist; |
| +CertPolicyEnforcer::~CertPolicyEnforcer() { |
| } |
| -bool CertPolicyEnforcer::HasRequiredNumberOfSCTs( |
| +bool CertPolicyEnforcer::DoesConformToCTEVPolicy( |
| X509Certificate* cert, |
| - const ct::CTVerifyResult& ct_result) { |
| - // TODO(eranm): Count the number of *independent* SCTs once the information |
| - // about log operators is available, crbug.com/425174 |
| - size_t num_valid_scts = ct_result.verified_scts.size(); |
| - size_t num_embedded_scts = |
| - std::count_if(ct_result.verified_scts.begin(), |
| - ct_result.verified_scts.end(), IsEmbeddedSCT); |
| + const ct::EVCertsWhitelist* ev_whitelist, |
| + const ct::CTVerifyResult& ct_result, |
| + const BoundNetLog& net_log) { |
| + ComplianceDetails details; |
| - size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts; |
| - // If at least two valid SCTs were delivered by means other than embedding |
| - // (i.e. in a TLS extension or OCSP), then the certificate conforms to bullet |
| - // number 3 of the "Qualifying Certificate" section of the CT/EV policy. |
| - if (num_non_embedded_scts >= 2) |
| + if (require_ct_for_ev_) |
| + CheckCTEVPolicyCompliance(cert, ev_whitelist, ct_result, &details); |
| + |
| + NetLog::ParametersCallback net_log_callback = |
| + base::Bind(&NetLogComplianceCheckResultCallback, base::Unretained(cert), |
| + base::Unretained(&details)); |
| + |
| + net_log.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED, |
| + net_log_callback); |
| + |
| + if (!details.ct_presence_required) |
| return true; |
| - if (cert->valid_start().is_null() || cert->valid_expiry().is_null() || |
| - cert->valid_start().is_max() || cert->valid_expiry().is_max()) { |
| - // Will not be able to calculate the certificate's validity period. |
| + if (!details.build_timely) |
| return false; |
| - } |
| - uint32_t expiry_in_months_approx = |
| - ApproximateMonthDifference(cert->valid_start(), cert->valid_expiry()); |
| + LogCTComplianceStatusToUMA(details.status); |
| - // For embedded SCTs, if the certificate has the number of SCTs specified in |
| - // table 1 of the "Qualifying Certificate" section of the CT/EV policy, then |
| - // it qualifies. |
| - size_t num_required_embedded_scts; |
| - if (expiry_in_months_approx > 39) { |
| - num_required_embedded_scts = 5; |
| - } else if (expiry_in_months_approx > 27) { |
| - num_required_embedded_scts = 4; |
| - } else if (expiry_in_months_approx >= 15) { |
| - num_required_embedded_scts = 3; |
| - } else { |
| - num_required_embedded_scts = 2; |
| - } |
| + if (details.status == CT_IN_WHITELIST || details.status == CT_ENOUGH_SCTS) |
| + return true; |
| - return num_embedded_scts >= num_required_embedded_scts; |
| + return false; |
| } |
| } // namespace net |