Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: net/cert/cert_policy_enforcer.cc

Issue 782333002: Certificate Transparency: Adding finch and NetLog logging for EV certs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressing review comments Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/cert/cert_policy_enforcer.h ('k') | net/cert/cert_policy_enforcer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/cert/cert_policy_enforcer.h" 5 #include "net/cert/cert_policy_enforcer.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h"
9 #include "base/build_time.h" 10 #include "base/build_time.h"
11 #include "base/callback_helpers.h"
10 #include "base/metrics/field_trial.h" 12 #include "base/metrics/field_trial.h"
11 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
12 #include "base/numerics/safe_conversions.h" 14 #include "base/numerics/safe_conversions.h"
13 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
16 #include "base/values.h"
17 #include "base/version.h"
18 #include "net/base/net_log.h"
14 #include "net/cert/ct_ev_whitelist.h" 19 #include "net/cert/ct_ev_whitelist.h"
15 #include "net/cert/ct_verify_result.h" 20 #include "net/cert/ct_verify_result.h"
16 #include "net/cert/signed_certificate_timestamp.h" 21 #include "net/cert/signed_certificate_timestamp.h"
17 #include "net/cert/x509_certificate.h" 22 #include "net/cert/x509_certificate.h"
23 #include "net/cert/x509_certificate_net_log_param.h"
18 24
19 namespace net { 25 namespace net {
20 26
21 namespace { 27 namespace {
22 28
23 bool IsEmbeddedSCT(const scoped_refptr<ct::SignedCertificateTimestamp>& sct) { 29 bool IsEmbeddedSCT(const scoped_refptr<ct::SignedCertificateTimestamp>& sct) {
24 return sct->origin == ct::SignedCertificateTimestamp::SCT_EMBEDDED; 30 return sct->origin == ct::SignedCertificateTimestamp::SCT_EMBEDDED;
25 } 31 }
26 32
27 // Returns true if the current build is recent enough to ensure that 33 // Returns true if the current build is recent enough to ensure that
(...skipping 18 matching lines...) Expand all
46 uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 + 52 uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 +
47 (exploded_expiry.month - exploded_start.month); 53 (exploded_expiry.month - exploded_start.month);
48 54
49 // Add any remainder as a full month. 55 // Add any remainder as a full month.
50 if (exploded_expiry.day_of_month > exploded_start.day_of_month) 56 if (exploded_expiry.day_of_month > exploded_start.day_of_month)
51 ++month_diff; 57 ++month_diff;
52 58
53 return month_diff; 59 return month_diff;
54 } 60 }
55 61
62 bool HasRequiredNumberOfSCTs(const X509Certificate& cert,
63 const ct::CTVerifyResult& ct_result) {
64 // TODO(eranm): Count the number of *independent* SCTs once the information
65 // about log operators is available, crbug.com/425174
66 size_t num_valid_scts = ct_result.verified_scts.size();
67 size_t num_embedded_scts =
68 std::count_if(ct_result.verified_scts.begin(),
69 ct_result.verified_scts.end(), IsEmbeddedSCT);
70
71 size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts;
72 // If at least two valid SCTs were delivered by means other than embedding
73 // (i.e. in a TLS extension or OCSP), then the certificate conforms to bullet
74 // number 3 of the "Qualifying Certificate" section of the CT/EV policy.
75 if (num_non_embedded_scts >= 2)
76 return true;
77
78 if (cert.valid_start().is_null() || cert.valid_expiry().is_null() ||
79 cert.valid_start().is_max() || cert.valid_expiry().is_max()) {
80 // Will not be able to calculate the certificate's validity period.
81 return false;
82 }
83
84 uint32_t expiry_in_months_approx =
85 ApproximateMonthDifference(cert.valid_start(), cert.valid_expiry());
86
87 // For embedded SCTs, if the certificate has the number of SCTs specified in
88 // table 1 of the "Qualifying Certificate" section of the CT/EV policy, then
89 // it qualifies.
90 size_t num_required_embedded_scts;
91 if (expiry_in_months_approx > 39) {
92 num_required_embedded_scts = 5;
93 } else if (expiry_in_months_approx > 27) {
94 num_required_embedded_scts = 4;
95 } else if (expiry_in_months_approx >= 15) {
96 num_required_embedded_scts = 3;
97 } else {
98 num_required_embedded_scts = 2;
99 }
100
101 return num_embedded_scts >= num_required_embedded_scts;
102 }
103
56 enum CTComplianceStatus { 104 enum CTComplianceStatus {
57 CT_NOT_COMPLIANT = 0, 105 CT_NOT_COMPLIANT = 0,
58 CT_IN_WHITELIST = 1, 106 CT_IN_WHITELIST = 1,
59 CT_ENOUGH_SCTS = 2, 107 CT_ENOUGH_SCTS = 2,
60 CT_COMPLIANCE_MAX, 108 CT_COMPLIANCE_MAX,
61 }; 109 };
62 110
111 const char* ComplianceStatusToString(CTComplianceStatus status) {
112 switch (status) {
113 case CT_NOT_COMPLIANT:
114 return "NOT_COMPLIANT";
115 break;
116 case CT_IN_WHITELIST:
117 return "WHITELISTED";
118 break;
119 case CT_ENOUGH_SCTS:
120 return "ENOUGH_SCTS";
121 break;
122 case CT_COMPLIANCE_MAX:
123 break;
124 }
125
126 return "unknown";
127 }
128
63 void LogCTComplianceStatusToUMA(CTComplianceStatus status) { 129 void LogCTComplianceStatusToUMA(CTComplianceStatus status) {
64 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status, 130 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status,
65 CT_COMPLIANCE_MAX); 131 CT_COMPLIANCE_MAX);
66 } 132 }
67 133
68 } // namespace 134 struct ComplianceDetails {
135 ComplianceDetails()
136 : ct_presence_required(false),
137 build_timely(false),
138 status(CT_NOT_COMPLIANT) {}
69 139
70 CertPolicyEnforcer::CertPolicyEnforcer(bool require_ct_for_ev) 140 // Whether enforcement of the policy was required or not.
71 : require_ct_for_ev_(require_ct_for_ev) { 141 bool ct_presence_required;
142 // Whether the build is not older than 10 weeks. The value is meaningful only
143 // if |ct_presence_required| is true.
144 bool build_timely;
145 // Compliance status - meaningful only if |ct_presence_required| and
146 // |build_timely| are true.
147 CTComplianceStatus status;
148 // EV whitelist version.
149 base::Version whitelist_version;
150 };
151
152 base::Value* NetLogComplianceCheckResultCallback(X509Certificate* cert,
153 ComplianceDetails* details,
154 NetLog::LogLevel log_level) {
155 base::DictionaryValue* dict = new base::DictionaryValue();
156 dict->Set("certificate", NetLogX509CertificateCallback(cert, log_level));
157 dict->SetBoolean("policy_enforcement_required",
158 details->ct_presence_required);
159 if (details->ct_presence_required) {
160 dict->SetBoolean("build_timely", details->build_timely);
161 if (details->build_timely) {
162 dict->SetString("ct_compliance_status",
163 ComplianceStatusToString(details->status));
164 if (details->whitelist_version.IsValid())
165 dict->SetString("ev_whitelist_version",
166 details->whitelist_version.GetString());
167 }
168 }
169 return dict;
72 } 170 }
73 171
74 CertPolicyEnforcer::~CertPolicyEnforcer() { 172 bool IsCertificateInWhitelist(const X509Certificate& cert,
75 } 173 const ct::EVCertsWhitelist* ev_whitelist) {
76
77 bool CertPolicyEnforcer::DoesConformToCTEVPolicy(
78 X509Certificate* cert,
79 const ct::EVCertsWhitelist* ev_whitelist,
80 const ct::CTVerifyResult& ct_result) {
81 if (!require_ct_for_ev_)
82 return true;
83
84 if (!IsBuildTimely())
85 return false;
86
87 if (IsCertificateInWhitelist(cert, ev_whitelist)) {
88 LogCTComplianceStatusToUMA(CT_IN_WHITELIST);
89 return true;
90 }
91
92 if (HasRequiredNumberOfSCTs(cert, ct_result)) {
93 LogCTComplianceStatusToUMA(CT_ENOUGH_SCTS);
94 return true;
95 }
96
97 LogCTComplianceStatusToUMA(CT_NOT_COMPLIANT);
98 return false;
99 }
100
101 bool CertPolicyEnforcer::IsCertificateInWhitelist(
102 X509Certificate* cert,
103 const ct::EVCertsWhitelist* ev_whitelist) {
104 bool cert_in_ev_whitelist = false; 174 bool cert_in_ev_whitelist = false;
105 if (ev_whitelist && ev_whitelist->IsValid()) { 175 if (ev_whitelist && ev_whitelist->IsValid()) {
106 const SHA256HashValue fingerprint( 176 const SHA256HashValue fingerprint(
107 X509Certificate::CalculateFingerprint256(cert->os_cert_handle())); 177 X509Certificate::CalculateFingerprint256(cert.os_cert_handle()));
108 178
109 std::string truncated_fp = 179 std::string truncated_fp =
110 std::string(reinterpret_cast<const char*>(fingerprint.data), 8); 180 std::string(reinterpret_cast<const char*>(fingerprint.data), 8);
111 cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp); 181 cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp);
112 182
113 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist", 183 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist",
114 cert_in_ev_whitelist); 184 cert_in_ev_whitelist);
115 } 185 }
116 return cert_in_ev_whitelist; 186 return cert_in_ev_whitelist;
117 } 187 }
118 188
119 bool CertPolicyEnforcer::HasRequiredNumberOfSCTs( 189 void CheckCTEVPolicyCompliance(X509Certificate* cert,
190 const ct::EVCertsWhitelist* ev_whitelist,
191 const ct::CTVerifyResult& ct_result,
192 ComplianceDetails* result) {
193 result->ct_presence_required = true;
194
195 if (!IsBuildTimely())
196 return;
197 result->build_timely = true;
198
199 if (ev_whitelist && ev_whitelist->IsValid())
200 result->whitelist_version = ev_whitelist->Version();
201
202 if (IsCertificateInWhitelist(*cert, ev_whitelist)) {
203 result->status = CT_IN_WHITELIST;
204 return;
205 }
206
207 if (HasRequiredNumberOfSCTs(*cert, ct_result)) {
208 result->status = CT_ENOUGH_SCTS;
209 return;
210 }
211
212 result->status = CT_NOT_COMPLIANT;
213 }
214
215 } // namespace
216
217 CertPolicyEnforcer::CertPolicyEnforcer(bool require_ct_for_ev)
218 : require_ct_for_ev_(require_ct_for_ev) {
219 }
220
221 CertPolicyEnforcer::~CertPolicyEnforcer() {
222 }
223
224 bool CertPolicyEnforcer::DoesConformToCTEVPolicy(
120 X509Certificate* cert, 225 X509Certificate* cert,
121 const ct::CTVerifyResult& ct_result) { 226 const ct::EVCertsWhitelist* ev_whitelist,
122 // TODO(eranm): Count the number of *independent* SCTs once the information 227 const ct::CTVerifyResult& ct_result,
123 // about log operators is available, crbug.com/425174 228 const BoundNetLog& net_log) {
124 size_t num_valid_scts = ct_result.verified_scts.size(); 229 ComplianceDetails details;
125 size_t num_embedded_scts =
126 std::count_if(ct_result.verified_scts.begin(),
127 ct_result.verified_scts.end(), IsEmbeddedSCT);
128 230
129 size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts; 231 if (require_ct_for_ev_)
130 // If at least two valid SCTs were delivered by means other than embedding 232 CheckCTEVPolicyCompliance(cert, ev_whitelist, ct_result, &details);
131 // (i.e. in a TLS extension or OCSP), then the certificate conforms to bullet 233
132 // number 3 of the "Qualifying Certificate" section of the CT/EV policy. 234 NetLog::ParametersCallback net_log_callback =
133 if (num_non_embedded_scts >= 2) 235 base::Bind(&NetLogComplianceCheckResultCallback, base::Unretained(cert),
236 base::Unretained(&details));
237
238 net_log.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED,
239 net_log_callback);
240
241 if (!details.ct_presence_required)
134 return true; 242 return true;
135 243
136 if (cert->valid_start().is_null() || cert->valid_expiry().is_null() || 244 if (!details.build_timely)
137 cert->valid_start().is_max() || cert->valid_expiry().is_max()) {
138 // Will not be able to calculate the certificate's validity period.
139 return false; 245 return false;
140 }
141 246
142 uint32_t expiry_in_months_approx = 247 LogCTComplianceStatusToUMA(details.status);
143 ApproximateMonthDifference(cert->valid_start(), cert->valid_expiry());
144 248
145 // For embedded SCTs, if the certificate has the number of SCTs specified in 249 if (details.status == CT_IN_WHITELIST || details.status == CT_ENOUGH_SCTS)
146 // table 1 of the "Qualifying Certificate" section of the CT/EV policy, then 250 return true;
147 // it qualifies.
148 size_t num_required_embedded_scts;
149 if (expiry_in_months_approx > 39) {
150 num_required_embedded_scts = 5;
151 } else if (expiry_in_months_approx > 27) {
152 num_required_embedded_scts = 4;
153 } else if (expiry_in_months_approx >= 15) {
154 num_required_embedded_scts = 3;
155 } else {
156 num_required_embedded_scts = 2;
157 }
158 251
159 return num_embedded_scts >= num_required_embedded_scts; 252 return false;
160 } 253 }
161 254
162 } // namespace net 255 } // namespace net
OLDNEW
« no previous file with comments | « net/cert/cert_policy_enforcer.h ('k') | net/cert/cert_policy_enforcer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698