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..25e9325fd4309f954bb171c2c05d648897ad94c0 100644 |
--- a/net/cert/cert_policy_enforcer.cc |
+++ b/net/cert/cert_policy_enforcer.cc |
@@ -6,15 +6,21 @@ |
#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 "base/version.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 +59,48 @@ uint32_t ApproximateMonthDifference(const base::Time& start, |
return month_diff; |
} |
+bool HasRequiredNumberOfSCTs(const 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); |
+ |
+ 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,51 +108,73 @@ enum CTComplianceStatus { |
CT_COMPLIANCE_MAX, |
}; |
-void LogCTComplianceStatusToUMA(CTComplianceStatus status) { |
- UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status, |
- CT_COMPLIANCE_MAX); |
-} |
- |
-} // namespace |
+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; |
+ } |
-CertPolicyEnforcer::CertPolicyEnforcer(bool require_ct_for_ev) |
- : require_ct_for_ev_(require_ct_for_ev) { |
+ return "unknown"; |
} |
-CertPolicyEnforcer::~CertPolicyEnforcer() { |
+void LogCTComplianceStatusToUMA(CTComplianceStatus status) { |
+ UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status, |
+ CT_COMPLIANCE_MAX); |
} |
-bool CertPolicyEnforcer::DoesConformToCTEVPolicy( |
- X509Certificate* cert, |
- const ct::EVCertsWhitelist* ev_whitelist, |
- const ct::CTVerifyResult& ct_result) { |
- if (!require_ct_for_ev_) |
- return true; |
- |
- if (!IsBuildTimely()) |
- return false; |
- |
- if (IsCertificateInWhitelist(cert, ev_whitelist)) { |
- LogCTComplianceStatusToUMA(CT_IN_WHITELIST); |
- return true; |
- } |
+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; |
+}; |
- if (HasRequiredNumberOfSCTs(cert, ct_result)) { |
- LogCTComplianceStatusToUMA(CT_ENOUGH_SCTS); |
- return true; |
+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()); |
+ } |
} |
- |
- LogCTComplianceStatusToUMA(CT_NOT_COMPLIANT); |
- return false; |
+ return dict; |
} |
-bool CertPolicyEnforcer::IsCertificateInWhitelist( |
- X509Certificate* cert, |
- const ct::EVCertsWhitelist* ev_whitelist) { |
+bool IsCertificateInWhitelist(const 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())); |
+ X509Certificate::CalculateFingerprint256(cert.os_cert_handle())); |
std::string truncated_fp = |
std::string(reinterpret_cast<const char*>(fingerprint.data), 8); |
@@ -116,47 +186,70 @@ bool CertPolicyEnforcer::IsCertificateInWhitelist( |
return cert_in_ev_whitelist; |
} |
-bool CertPolicyEnforcer::HasRequiredNumberOfSCTs( |
+void CheckCTEVPolicyCompliance(X509Certificate* cert, |
+ const ct::EVCertsWhitelist* ev_whitelist, |
+ const ct::CTVerifyResult& ct_result, |
+ ComplianceDetails* result) { |
+ result->ct_presence_required = true; |
+ |
+ if (!IsBuildTimely()) |
+ return; |
+ result->build_timely = true; |
+ |
+ if (ev_whitelist && ev_whitelist->IsValid()) |
+ result->whitelist_version = ev_whitelist->Version(); |
+ |
+ if (IsCertificateInWhitelist(*cert, ev_whitelist)) { |
+ result->status = CT_IN_WHITELIST; |
+ return; |
+ } |
+ |
+ if (HasRequiredNumberOfSCTs(*cert, ct_result)) { |
+ result->status = CT_ENOUGH_SCTS; |
+ return; |
+ } |
+ |
+ result->status = CT_NOT_COMPLIANT; |
+} |
+ |
+} // namespace |
+ |
+CertPolicyEnforcer::CertPolicyEnforcer(bool require_ct_for_ev) |
+ : require_ct_for_ev_(require_ct_for_ev) { |
+} |
+ |
+CertPolicyEnforcer::~CertPolicyEnforcer() { |
+} |
+ |
+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 |