diff options
author | eranm <eranm@chromium.org> | 2014-12-18 00:43:23 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-12-18 08:43:43 +0000 |
commit | 18a019275b2995f77fbc95691c5784fb2ca83836 (patch) | |
tree | 4848442607b368a393c9a1c24d794cd660c7676f | |
parent | c97aaa143edd02f4e87f0197315fb60c2388c50c (diff) | |
download | chromium_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.cc | 16 | ||||
-rw-r--r-- | chrome/browser/io_thread.cc | 19 | ||||
-rw-r--r-- | chrome/browser/net/packed_ct_ev_whitelist.cc | 8 | ||||
-rw-r--r-- | chrome/browser/net/packed_ct_ev_whitelist.h | 8 | ||||
-rw-r--r-- | chrome/browser/net/packed_ct_ev_whitelist_unittest.cc | 8 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/log_view_painter.js | 46 | ||||
-rw-r--r-- | net/base/net_log_event_type_list.h | 14 | ||||
-rw-r--r-- | net/cert/cert_policy_enforcer.cc | 225 | ||||
-rw-r--r-- | net/cert/cert_policy_enforcer.h | 13 | ||||
-rw-r--r-- | net/cert/cert_policy_enforcer_unittest.cc | 38 | ||||
-rw-r--r-- | net/cert/ct_ev_whitelist.h | 9 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 2 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_openssl.cc | 2 |
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() |