summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreranm <eranm@chromium.org>2014-12-18 00:43:23 -0800
committerCommit bot <commit-bot@chromium.org>2014-12-18 08:43:43 +0000
commit18a019275b2995f77fbc95691c5784fb2ca83836 (patch)
tree4848442607b368a393c9a1c24d794cd660c7676f
parentc97aaa143edd02f4e87f0197315fb60c2388c50c (diff)
downloadchromium_src-18a019275b2995f77fbc95691c5784fb2ca83836.zip
chromium_src-18a019275b2995f77fbc95691c5784fb2ca83836.tar.gz
chromium_src-18a019275b2995f77fbc95691c5784fb2ca83836.tar.bz2
Certificate Transparency: Adding finch and NetLog logging for EV certs
This is a follow-up patch to address some of rsleevi@'s comments on a previous patch that policy compliance checks for EV certificates. The remaining todo (adding an integration test to SSLClientSocket*) will be addressed in a separate patch. BUG=437766 Review URL: https://codereview.chromium.org/782333002 Cr-Commit-Position: refs/heads/master@{#308971}
-rw-r--r--chrome/browser/component_updater/ev_whitelist_component_installer.cc16
-rw-r--r--chrome/browser/io_thread.cc19
-rw-r--r--chrome/browser/net/packed_ct_ev_whitelist.cc8
-rw-r--r--chrome/browser/net/packed_ct_ev_whitelist.h8
-rw-r--r--chrome/browser/net/packed_ct_ev_whitelist_unittest.cc8
-rw-r--r--chrome/browser/resources/net_internals/log_view_painter.js46
-rw-r--r--net/base/net_log_event_type_list.h14
-rw-r--r--net/cert/cert_policy_enforcer.cc225
-rw-r--r--net/cert/cert_policy_enforcer.h13
-rw-r--r--net/cert/cert_policy_enforcer_unittest.cc38
-rw-r--r--net/cert/ct_ev_whitelist.h9
-rw-r--r--net/socket/ssl_client_socket_nss.cc2
-rw-r--r--net/socket/ssl_client_socket_openssl.cc2
13 files changed, 278 insertions, 130 deletions
diff --git a/chrome/browser/component_updater/ev_whitelist_component_installer.cc b/chrome/browser/component_updater/ev_whitelist_component_installer.cc
index 4f30bdb..bb5881c 100644
--- a/chrome/browser/component_updater/ev_whitelist_component_installer.cc
+++ b/chrome/browser/component_updater/ev_whitelist_component_installer.cc
@@ -14,6 +14,7 @@
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/path_service.h"
+#include "base/version.h"
#include "chrome/browser/net/packed_ct_ev_whitelist.h"
#include "components/component_updater/component_updater_paths.h"
#include "content/public/browser/browser_thread.h"
@@ -30,7 +31,8 @@ base::FilePath GetEVWhitelistFilePath(const base::FilePath& base_path) {
}
void UpdateNewWhitelistData(const base::FilePath& new_whitelist_file,
- const base::FilePath& stored_whitelist_path) {
+ const base::FilePath& stored_whitelist_path,
+ const base::Version& version) {
VLOG(1) << "Reading new EV whitelist from file: "
<< new_whitelist_file.value();
std::string compressed_list;
@@ -40,7 +42,7 @@ void UpdateNewWhitelistData(const base::FilePath& new_whitelist_file,
}
scoped_refptr<net::ct::EVCertsWhitelist> new_whitelist(
- new PackedEVCertsWhitelist(compressed_list));
+ new PackedEVCertsWhitelist(compressed_list, version));
if (!new_whitelist->IsValid()) {
VLOG(1) << "Failed uncompressing EV certs whitelist.";
return;
@@ -70,8 +72,12 @@ void DoInitialLoadFromDisk(const base::FilePath& stored_whitelist_path) {
return;
}
+ // The version number is unknown as the list is loaded from disk, not
+ // the component.
+ // In practice very quickly the component updater will call ComponentReady
+ // which will have a valid version.
scoped_refptr<net::ct::EVCertsWhitelist> new_whitelist(
- new PackedEVCertsWhitelist(compressed_list));
+ new PackedEVCertsWhitelist(compressed_list, Version()));
if (!new_whitelist->IsValid()) {
VLOG(1) << "Failed uncompressing EV certs whitelist.";
return;
@@ -128,8 +134,8 @@ void EVWhitelistComponentInstallerTraits::ComponentReady(
const base::FilePath whitelist_file = GetInstalledPath(path);
content::BrowserThread::PostBlockingPoolTask(
- FROM_HERE,
- base::Bind(&UpdateNewWhitelistData, whitelist_file, ev_whitelist_path_));
+ FROM_HERE, base::Bind(&UpdateNewWhitelistData, whitelist_file,
+ ev_whitelist_path_, version));
}
bool EVWhitelistComponentInstallerTraits::VerifyInstallation(
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index f88e506..8f34934 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -324,6 +324,17 @@ bool IsStaleWhileRevalidateEnabled(const base::CommandLine& command_line) {
return group_name == "Enabled";
}
+bool IsCertificateTransparencyRequiredForEV(
+ const base::CommandLine& command_line) {
+ const std::string group_name =
+ base::FieldTrialList::FindFullName("CTRequiredForEVTrial");
+
+ if (command_line.HasSwitch(switches::kRequireCTForEV))
+ return true;
+
+ return group_name == "RequirementEnforced";
+}
+
} // namespace
class IOThread::LoggingNetworkChangeObserver
@@ -656,12 +667,8 @@ void IOThread::InitAsync() {
}
net::CertPolicyEnforcer* policy_enforcer = NULL;
- // TODO(eranm): Control with Finch, crbug.com/437766
- if (command_line.HasSwitch(switches::kRequireCTForEV)) {
- policy_enforcer = new net::CertPolicyEnforcer(true);
- } else {
- policy_enforcer = new net::CertPolicyEnforcer(false);
- }
+ policy_enforcer = new net::CertPolicyEnforcer(
+ IsCertificateTransparencyRequiredForEV(command_line));
globals_->cert_policy_enforcer.reset(policy_enforcer);
globals_->ssl_config_service = GetSSLConfigService();
diff --git a/chrome/browser/net/packed_ct_ev_whitelist.cc b/chrome/browser/net/packed_ct_ev_whitelist.cc
index 402838d..685ccbb 100644
--- a/chrome/browser/net/packed_ct_ev_whitelist.cc
+++ b/chrome/browser/net/packed_ct_ev_whitelist.cc
@@ -98,7 +98,9 @@ bool PackedEVCertsWhitelist::UncompressEVWhitelist(
}
PackedEVCertsWhitelist::PackedEVCertsWhitelist(
- const std::string& compressed_whitelist) {
+ const std::string& compressed_whitelist,
+ const base::Version& version)
+ : version_(version) {
if (!UncompressEVWhitelist(compressed_whitelist, &whitelist_)) {
whitelist_.clear();
return;
@@ -124,3 +126,7 @@ bool PackedEVCertsWhitelist::ContainsCertificateHash(
bool PackedEVCertsWhitelist::IsValid() const {
return whitelist_.size() > 0;
}
+
+base::Version PackedEVCertsWhitelist::Version() const {
+ return version_;
+}
diff --git a/chrome/browser/net/packed_ct_ev_whitelist.h b/chrome/browser/net/packed_ct_ev_whitelist.h
index 6cbc545..6520f3f 100644
--- a/chrome/browser/net/packed_ct_ev_whitelist.h
+++ b/chrome/browser/net/packed_ct_ev_whitelist.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/gtest_prod_util.h"
+#include "base/version.h"
#include "net/cert/ct_ev_whitelist.h"
namespace base {
@@ -30,7 +31,8 @@ class PackedEVCertsWhitelist : public net::ct::EVCertsWhitelist {
public:
// Unpacks the given |compressed_whitelist|. See the class documentation
// for description of the |compressed_whitelist| format.
- explicit PackedEVCertsWhitelist(const std::string& compressed_whitelist);
+ PackedEVCertsWhitelist(const std::string& compressed_whitelist,
+ const base::Version& version);
// Returns true if the |certificate_hash| appears in the EV certificate hashes
// whitelist. Must not be called if IsValid for this instance returned false.
@@ -41,6 +43,9 @@ class PackedEVCertsWhitelist : public net::ct::EVCertsWhitelist {
// was valid, false otherwise.
bool IsValid() const override;
+ // Returns the version of the whitelist in use, if available.
+ base::Version Version() const override;
+
protected:
~PackedEVCertsWhitelist() override;
@@ -67,6 +72,7 @@ class PackedEVCertsWhitelist : public net::ct::EVCertsWhitelist {
// shows that bsearch is about twice as fast as std::set lookups (and std::set
// has additional memory overhead).
std::vector<uint64_t> whitelist_;
+ base::Version version_;
DISALLOW_COPY_AND_ASSIGN(PackedEVCertsWhitelist);
};
diff --git a/chrome/browser/net/packed_ct_ev_whitelist_unittest.cc b/chrome/browser/net/packed_ct_ev_whitelist_unittest.cc
index 346d659..7877e95 100644
--- a/chrome/browser/net/packed_ct_ev_whitelist_unittest.cc
+++ b/chrome/browser/net/packed_ct_ev_whitelist_unittest.cc
@@ -119,7 +119,7 @@ TEST(PackedEVCertsWhitelistTest, UncompressesWhitelistCorrectly) {
TEST(PackedEVCertsWhitelistTest, CanFindHashInSetList) {
scoped_refptr<PackedEVCertsWhitelist> whitelist(
- new PackedEVCertsWhitelist(GetAllWhitelistData()));
+ new PackedEVCertsWhitelist(GetAllWhitelistData(), base::Version()));
EXPECT_TRUE(whitelist->IsValid());
EXPECT_TRUE(whitelist->ContainsCertificateHash(GetFirstHash()));
@@ -129,21 +129,21 @@ TEST(PackedEVCertsWhitelistTest, CanFindHashInSetList) {
TEST(PackedEVCertsWhitelistTest, CorrectlyIdentifiesEmptyWhitelistIsInvalid) {
scoped_refptr<PackedEVCertsWhitelist> whitelist(
- new PackedEVCertsWhitelist(""));
+ new PackedEVCertsWhitelist(std::string(), base::Version()));
EXPECT_FALSE(whitelist->IsValid());
}
TEST(PackedEVCertsWhitelistTest, CorrectlyIdentifiesPartialWhitelistIsInvalid) {
scoped_refptr<PackedEVCertsWhitelist> whitelist(
- new PackedEVCertsWhitelist(GetPartialWhitelistData(14)));
+ new PackedEVCertsWhitelist(GetPartialWhitelistData(14), base::Version()));
EXPECT_FALSE(whitelist->IsValid());
}
TEST(PackedEVCertsWhitelistTest, CorrectlyIdentifiesWhitelistIsValid) {
scoped_refptr<PackedEVCertsWhitelist> whitelist(
- new PackedEVCertsWhitelist(GetAllWhitelistData()));
+ new PackedEVCertsWhitelist(GetAllWhitelistData(), base::Version()));
EXPECT_TRUE(whitelist->IsValid());
}
diff --git a/chrome/browser/resources/net_internals/log_view_painter.js b/chrome/browser/resources/net_internals/log_view_painter.js
index 38b70f8..7565cd1 100644
--- a/chrome/browser/resources/net_internals/log_view_painter.js
+++ b/chrome/browser/resources/net_internals/log_view_painter.js
@@ -281,6 +281,8 @@ function getParamaterWriterForEventType(eventType) {
case EventType.CERT_VERIFIER_JOB:
case EventType.SSL_CERTIFICATES_RECEIVED:
return writeParamsForCertificates;
+ case EventType.EV_CERT_CT_COMPLIANCE_CHECKED:
+ return writeParamsForCheckedEVCertificates;
case EventType.SSL_VERSION_FALLBACK:
return writeParamsForSSLVersionFallback;
@@ -585,30 +587,28 @@ function writeParamsForRequestHeaders(entry, out, consumedParams) {
consumedParams.headers = true;
}
+function writeCertificateParam(
+ certs_container, out, consumedParams, paramName) {
+ if (certs_container.certificates instanceof Array) {
+ var certs = certs_container.certificates.reduce(
+ function(previous, current) {
+ return previous.concat(current.split('\n'));
+ }, new Array());
+ out.writeArrowKey(paramName);
+ out.writeSpaceIndentedLines(8, certs);
+ consumedParams[paramName] = true;
+ }
+}
+
/**
* Outputs the certificate parameters of |entry| to |out|.
*/
function writeParamsForCertificates(entry, out, consumedParams) {
- if (entry.params.certificates instanceof Array) {
- var certs = entry.params.certificates.reduce(function(previous, current) {
- return previous.concat(current.split('\n'));
- }, new Array());
- out.writeArrowKey('certificates');
- out.writeSpaceIndentedLines(8, certs);
- consumedParams.certificates = true;
- }
-
- if (typeof(entry.params.verified_cert) == 'object') {
- if (entry.params.verified_cert.certificates instanceof Array) {
- var certs = entry.params.verified_cert.certificates.reduce(
- function(previous, current) {
- return previous.concat(current.split('\n'));
- }, new Array());
- out.writeArrowKey('verified_cert');
- out.writeSpaceIndentedLines(8, certs);
- consumedParams.verified_cert = true;
- }
- }
+ writeCertificateParam(entry.params, out, consumedParams, 'certificates');
+
+ if (typeof(entry.params.verified_cert) == 'object')
+ writeCertificateParam(
+ entry.params.verified_cert, out, consumedParams, 'verified_cert');
if (typeof(entry.params.cert_status) == 'number') {
var valueStr = entry.params.cert_status + ' (' +
@@ -619,6 +619,12 @@ function writeParamsForCertificates(entry, out, consumedParams) {
}
+function writeParamsForCheckedEVCertificates(entry, out, consumedParams) {
+ if (typeof(entry.params.certificate) == 'object')
+ writeCertificateParam(
+ entry.params.certificate, out, consumedParams, 'certificate');
+}
+
/**
* Outputs the SSL version fallback parameters of |entry| to |out|.
*/
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h
index ce30360..c110a3d 100644
--- a/net/base/net_log_event_type_list.h
+++ b/net/base/net_log_event_type_list.h
@@ -584,6 +584,20 @@ EVENT_TYPE(SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED)
// }
EVENT_TYPE(SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED)
+// The EV certificate was checked for compliance with Certificate Transparency
+// requirements.
+//
+// The following parameters are attached to the event:
+// {
+// "certificate": <An X.509 certificate, same format as in
+// CERT_VERIFIER_JOB.>
+// "policy_enforcement_required": <boolean>
+// "build_timely": <boolean>
+// "ct_compliance_status": <string describing compliance status>
+// "ev_whitelist_version": <optional; string representing whitelist version>
+// }
+EVENT_TYPE(EV_CERT_CT_COMPLIANCE_CHECKED)
+
// ------------------------------------------------------------------------
// DatagramSocket
// ------------------------------------------------------------------------
diff --git a/net/cert/cert_policy_enforcer.cc b/net/cert/cert_policy_enforcer.cc
index 0d05366..25e9325 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
diff --git a/net/cert/cert_policy_enforcer.h b/net/cert/cert_policy_enforcer.h
index e6c7960..5d6b64b 100644
--- a/net/cert/cert_policy_enforcer.h
+++ b/net/cert/cert_policy_enforcer.h
@@ -7,6 +7,7 @@
#include <stddef.h>
#include "net/base/net_export.h"
+#include "net/base/net_log.h"
namespace net {
@@ -30,21 +31,17 @@ class NET_EXPORT CertPolicyEnforcer {
virtual ~CertPolicyEnforcer();
// Returns true if the collection of SCTs for the given certificate
- // conforms with the CT/EV policy.
+ // conforms with the CT/EV policy. Conformance details are logged to
+ // |net_log|.
// |cert| is the certificate for which the SCTs apply.
// |ct_result| must contain the result of verifying any SCTs associated with
// |cert| prior to invoking this method.
bool DoesConformToCTEVPolicy(X509Certificate* cert,
const ct::EVCertsWhitelist* ev_whitelist,
- const ct::CTVerifyResult& ct_result);
+ const ct::CTVerifyResult& ct_result,
+ const BoundNetLog& net_log);
private:
- bool IsCertificateInWhitelist(X509Certificate* cert,
- const ct::EVCertsWhitelist* ev_whitelist);
-
- bool HasRequiredNumberOfSCTs(X509Certificate* cert,
- const ct::CTVerifyResult& ct_result);
-
bool require_ct_for_ev_;
};
diff --git a/net/cert/cert_policy_enforcer_unittest.cc b/net/cert/cert_policy_enforcer_unittest.cc
index de840c6..9299761 100644
--- a/net/cert/cert_policy_enforcer_unittest.cc
+++ b/net/cert/cert_policy_enforcer_unittest.cc
@@ -7,6 +7,7 @@
#include <string>
#include "base/memory/scoped_ptr.h"
+#include "base/version.h"
#include "net/base/test_data_directory.h"
#include "net/cert/ct_ev_whitelist.h"
#include "net/cert/ct_verify_result.h"
@@ -33,6 +34,8 @@ class DummyEVCertsWhitelist : public ct::EVCertsWhitelist {
return canned_contains_response_;
}
+ base::Version Version() const override { return base::Version(); }
+
protected:
~DummyEVCertsWhitelist() override {}
@@ -74,8 +77,8 @@ TEST_F(CertPolicyEnforcerTest, ConformsToCTEVPolicyWithNonEmbeddedSCTs) {
FillResultWithSCTsOfOrigin(
ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, 2, &result);
- EXPECT_TRUE(
- policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr, result));
+ EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr,
+ result, BoundNetLog()));
}
TEST_F(CertPolicyEnforcerTest, ConformsToCTEVPolicyWithEmbeddedSCTs) {
@@ -84,8 +87,8 @@ TEST_F(CertPolicyEnforcerTest, ConformsToCTEVPolicyWithEmbeddedSCTs) {
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 5,
&result);
- EXPECT_TRUE(
- policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr, result));
+ EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr,
+ result, BoundNetLog()));
}
TEST_F(CertPolicyEnforcerTest, DoesNotConformToCTEVPolicyNotEnoughSCTs) {
@@ -99,13 +102,13 @@ TEST_F(CertPolicyEnforcerTest, DoesNotConformToCTEVPolicyNotEnoughSCTs) {
&result);
EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
- chain_.get(), non_including_whitelist.get(), result));
+ chain_.get(), non_including_whitelist.get(), result, BoundNetLog()));
// ... but should be OK if whitelisted.
scoped_refptr<ct::EVCertsWhitelist> whitelist(
new DummyEVCertsWhitelist(true, true));
EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
- chain_.get(), whitelist.get(), result));
+ chain_.get(), whitelist.get(), result, BoundNetLog()));
}
TEST_F(CertPolicyEnforcerTest, DoesNotEnforceCTPolicyIfNotRequired) {
@@ -116,7 +119,8 @@ TEST_F(CertPolicyEnforcerTest, DoesNotEnforceCTPolicyIfNotRequired) {
&result);
// Expect true despite the chain not having enough SCTs as the policy
// is not enforced.
- EXPECT_TRUE(enforcer->DoesConformToCTEVPolicy(chain_.get(), nullptr, result));
+ EXPECT_TRUE(enforcer->DoesConformToCTEVPolicy(chain_.get(), nullptr, result,
+ BoundNetLog()));
}
TEST_F(CertPolicyEnforcerTest, DoesNotConformToPolicyInvalidDates) {
@@ -126,12 +130,12 @@ TEST_F(CertPolicyEnforcerTest, DoesNotConformToPolicyInvalidDates) {
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 5,
&result);
EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
- no_valid_dates_cert.get(), nullptr, result));
+ no_valid_dates_cert.get(), nullptr, result, BoundNetLog()));
// ... but should be OK if whitelisted.
scoped_refptr<ct::EVCertsWhitelist> whitelist(
new DummyEVCertsWhitelist(true, true));
EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
- chain_.get(), whitelist.get(), result));
+ chain_.get(), whitelist.get(), result, BoundNetLog()));
}
TEST_F(CertPolicyEnforcerTest,
@@ -152,15 +156,15 @@ TEST_F(CertPolicyEnforcerTest,
for (size_t j = 0; j < curr_required_scts - 1; ++j) {
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
1, &result);
- EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(cert.get(),
- nullptr, result))
+ EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
+ cert.get(), nullptr, result, BoundNetLog()))
<< " for: " << curr_validity << " and " << curr_required_scts
<< " scts=" << result.verified_scts.size() << " j=" << j;
}
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
&result);
- EXPECT_TRUE(
- policy_enforcer_->DoesConformToCTEVPolicy(cert.get(), nullptr, result));
+ EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
+ cert.get(), nullptr, result, BoundNetLog()));
}
}
@@ -172,7 +176,7 @@ TEST_F(CertPolicyEnforcerTest, ConformsToPolicyByEVWhitelistPresence) {
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
&result);
EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
- chain_.get(), whitelist.get(), result));
+ chain_.get(), whitelist.get(), result, BoundNetLog()));
}
TEST_F(CertPolicyEnforcerTest, IgnoresInvalidEVWhitelist) {
@@ -183,15 +187,15 @@ TEST_F(CertPolicyEnforcerTest, IgnoresInvalidEVWhitelist) {
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
&result);
EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
- chain_.get(), whitelist.get(), result));
+ chain_.get(), whitelist.get(), result, BoundNetLog()));
}
TEST_F(CertPolicyEnforcerTest, IgnoresNullEVWhitelist) {
ct::CTVerifyResult result;
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
&result);
- EXPECT_FALSE(
- policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr, result));
+ EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
+ chain_.get(), nullptr, result, BoundNetLog()));
}
} // namespace
diff --git a/net/cert/ct_ev_whitelist.h b/net/cert/ct_ev_whitelist.h
index a12b9d5..f31b7a1 100644
--- a/net/cert/ct_ev_whitelist.h
+++ b/net/cert/ct_ev_whitelist.h
@@ -10,6 +10,12 @@
#include "base/memory/ref_counted.h"
#include "net/base/net_export.h"
+namespace base {
+
+class Version;
+
+} // namespace base
+
namespace net {
namespace ct {
@@ -26,6 +32,9 @@ class NET_EXPORT EVCertsWhitelist
// false otherwise.
virtual bool IsValid() const = 0;
+ // Returns the version of the whitelist in use
+ virtual base::Version Version() const = 0;
+
protected:
virtual ~EVCertsWhitelist() {}
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 5e33e12..161184b 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -3584,7 +3584,7 @@ void SSLClientSocketNSS::VerifyCT() {
SSLConfigService::GetEVCertsWhitelist();
if (!policy_enforcer_->DoesConformToCTEVPolicy(
server_cert_verify_result_.verified_cert.get(),
- ev_whitelist.get(), ct_verify_result_)) {
+ ev_whitelist.get(), ct_verify_result_, net_log_)) {
// TODO(eranm): Log via the BoundNetLog, see crbug.com/437766
VLOG(1) << "EV certificate for "
<< server_cert_verify_result_.verified_cert->subject()
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index de74b04..74ac8fc 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -1286,7 +1286,7 @@ void SSLClientSocketOpenSSL::VerifyCT() {
SSLConfigService::GetEVCertsWhitelist();
if (!policy_enforcer_->DoesConformToCTEVPolicy(
server_cert_verify_result_.verified_cert.get(),
- ev_whitelist.get(), ct_verify_result_)) {
+ ev_whitelist.get(), ct_verify_result_, net_log_)) {
// TODO(eranm): Log via the BoundNetLog, see crbug.com/437766
VLOG(1) << "EV certificate for "
<< server_cert_verify_result_.verified_cert->subject()