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

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

Issue 1579233002: Rename CertPolicyEnforcer to CTPolicyEnforcer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 11 months 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "net/cert/cert_policy_enforcer.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/build_time.h"
12 #include "base/callback_helpers.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/time/time.h"
18 #include "base/values.h"
19 #include "base/version.h"
20 #include "net/cert/ct_ev_whitelist.h"
21 #include "net/cert/ct_known_logs.h"
22 #include "net/cert/ct_verify_result.h"
23 #include "net/cert/signed_certificate_timestamp.h"
24 #include "net/cert/x509_certificate.h"
25 #include "net/cert/x509_certificate_net_log_param.h"
26 #include "net/log/net_log.h"
27
28 namespace net {
29
30 namespace {
31
32 bool IsEmbeddedSCT(const scoped_refptr<ct::SignedCertificateTimestamp>& sct) {
33 return sct->origin == ct::SignedCertificateTimestamp::SCT_EMBEDDED;
34 }
35
36 // Returns true if the current build is recent enough to ensure that
37 // built-in security information (e.g. CT Logs) is fresh enough.
38 // TODO(eranm): Move to base or net/base
39 bool IsBuildTimely() {
40 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD)
41 return true;
42 #else
43 const base::Time build_time = base::GetBuildTime();
44 // We consider built-in information to be timely for 10 weeks.
45 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
46 #endif
47 }
48
49 bool IsGoogleIssuedSCT(
50 const scoped_refptr<ct::SignedCertificateTimestamp>& sct) {
51 return ct::IsLogOperatedByGoogle(sct->log_id);
52 }
53
54 // Returns a rounded-down months difference of |start| and |end|,
55 // together with an indication of whether the last month was
56 // a full month, because the range starts specified in the policy
57 // are not consistent in terms of including the range start value.
58 void RoundedDownMonthDifference(const base::Time& start,
59 const base::Time& end,
60 size_t* rounded_months_difference,
61 bool* has_partial_month) {
62 DCHECK(rounded_months_difference);
63 DCHECK(has_partial_month);
64 base::Time::Exploded exploded_start;
65 base::Time::Exploded exploded_expiry;
66 start.UTCExplode(&exploded_start);
67 end.UTCExplode(&exploded_expiry);
68 if (end < start) {
69 *rounded_months_difference = 0;
70 *has_partial_month = false;
71 }
72
73 *has_partial_month = true;
74 uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 +
75 (exploded_expiry.month - exploded_start.month);
76 if (exploded_expiry.day_of_month < exploded_start.day_of_month)
77 --month_diff;
78 else if (exploded_expiry.day_of_month == exploded_start.day_of_month)
79 *has_partial_month = false;
80
81 *rounded_months_difference = month_diff;
82 }
83
84 bool HasRequiredNumberOfSCTs(const X509Certificate& cert,
85 const ct::CTVerifyResult& ct_result) {
86 size_t num_valid_scts = ct_result.verified_scts.size();
87 size_t num_embedded_scts = base::checked_cast<size_t>(
88 std::count_if(ct_result.verified_scts.begin(),
89 ct_result.verified_scts.end(), IsEmbeddedSCT));
90
91 size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts;
92 // If at least two valid SCTs were delivered by means other than embedding
93 // (i.e. in a TLS extension or OCSP), then the certificate conforms to bullet
94 // number 3 of the "Qualifying Certificate" section of the CT/EV policy.
95 if (num_non_embedded_scts >= 2)
96 return true;
97
98 if (cert.valid_start().is_null() || cert.valid_expiry().is_null() ||
99 cert.valid_start().is_max() || cert.valid_expiry().is_max()) {
100 // Will not be able to calculate the certificate's validity period.
101 return false;
102 }
103
104 size_t lifetime;
105 bool has_partial_month;
106 RoundedDownMonthDifference(cert.valid_start(), cert.valid_expiry(), &lifetime,
107 &has_partial_month);
108
109 // For embedded SCTs, if the certificate has the number of SCTs specified in
110 // table 1 of the "Qualifying Certificate" section of the CT/EV policy, then
111 // it qualifies.
112 size_t num_required_embedded_scts;
113 if (lifetime > 39 || (lifetime == 39 && has_partial_month)) {
114 num_required_embedded_scts = 5;
115 } else if (lifetime > 27 || (lifetime == 27 && has_partial_month)) {
116 num_required_embedded_scts = 4;
117 } else if (lifetime >= 15) {
118 num_required_embedded_scts = 3;
119 } else {
120 num_required_embedded_scts = 2;
121 }
122
123 return num_embedded_scts >= num_required_embedded_scts;
124 }
125
126 // Returns true if |verified_scts| contains SCTs from at least one log that is
127 // operated by Google and at least one log that is not operated by Google. This
128 // is required for SCTs after July 1st, 2015, as documented at
129 // http://dev.chromium.org/Home/chromium-security/root-ca-policy/EVCTPlanMay2015 edition.pdf
130 bool HasEnoughDiverseSCTs(const ct::SCTList& verified_scts) {
131 size_t num_google_issued_scts = base::checked_cast<size_t>(std::count_if(
132 verified_scts.begin(), verified_scts.end(), IsGoogleIssuedSCT));
133 return (num_google_issued_scts > 0) &&
134 (verified_scts.size() != num_google_issued_scts);
135 }
136
137 enum CTComplianceStatus {
138 CT_NOT_COMPLIANT = 0,
139 CT_IN_WHITELIST = 1,
140 CT_ENOUGH_SCTS = 2,
141 CT_NOT_ENOUGH_DIVERSE_SCTS = 3,
142 CT_COMPLIANCE_MAX,
143 };
144
145 const char* ComplianceStatusToString(CTComplianceStatus status) {
146 switch (status) {
147 case CT_NOT_COMPLIANT:
148 return "NOT_COMPLIANT";
149 break;
150 case CT_IN_WHITELIST:
151 return "WHITELISTED";
152 break;
153 case CT_ENOUGH_SCTS:
154 return "ENOUGH_SCTS";
155 break;
156 case CT_NOT_ENOUGH_DIVERSE_SCTS:
157 return "NOT_ENOUGH_DIVERSE_SCTS";
158 break;
159 case CT_COMPLIANCE_MAX:
160 break;
161 }
162
163 return "unknown";
164 }
165
166 enum EVWhitelistStatus {
167 EV_WHITELIST_NOT_PRESENT = 0,
168 EV_WHITELIST_INVALID = 1,
169 EV_WHITELIST_VALID = 2,
170 EV_WHITELIST_MAX,
171 };
172
173 void LogCTComplianceStatusToUMA(CTComplianceStatus status,
174 const ct::EVCertsWhitelist* ev_whitelist) {
175 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status,
176 CT_COMPLIANCE_MAX);
177 if (status == CT_NOT_COMPLIANT) {
178 EVWhitelistStatus ev_whitelist_status = EV_WHITELIST_NOT_PRESENT;
179 if (ev_whitelist != NULL) {
180 if (ev_whitelist->IsValid())
181 ev_whitelist_status = EV_WHITELIST_VALID;
182 else
183 ev_whitelist_status = EV_WHITELIST_INVALID;
184 }
185
186 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVWhitelistValidityForNonCompliantCert",
187 ev_whitelist_status, EV_WHITELIST_MAX);
188 }
189 }
190
191 struct ComplianceDetails {
192 ComplianceDetails()
193 : ct_presence_required(false),
194 build_timely(false),
195 status(CT_NOT_COMPLIANT) {}
196
197 // Whether enforcement of the policy was required or not.
198 bool ct_presence_required;
199 // Whether the build is not older than 10 weeks. The value is meaningful only
200 // if |ct_presence_required| is true.
201 bool build_timely;
202 // Compliance status - meaningful only if |ct_presence_required| and
203 // |build_timely| are true.
204 CTComplianceStatus status;
205 // EV whitelist version.
206 base::Version whitelist_version;
207 };
208
209 scoped_ptr<base::Value> NetLogComplianceCheckResultCallback(
210 X509Certificate* cert,
211 ComplianceDetails* details,
212 NetLogCaptureMode capture_mode) {
213 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
214 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode));
215 dict->SetBoolean("policy_enforcement_required",
216 details->ct_presence_required);
217 if (details->ct_presence_required) {
218 dict->SetBoolean("build_timely", details->build_timely);
219 if (details->build_timely) {
220 dict->SetString("ct_compliance_status",
221 ComplianceStatusToString(details->status));
222 if (details->whitelist_version.IsValid())
223 dict->SetString("ev_whitelist_version",
224 details->whitelist_version.GetString());
225 }
226 }
227 return std::move(dict);
228 }
229
230 // Returns true if all SCTs in |verified_scts| were issued on, or after, the
231 // date specified in kDiverseSCTRequirementStartDate
232 bool AllSCTsPastDistinctSCTRequirementEnforcementDate(
233 const ct::SCTList& verified_scts) {
234 // The date when diverse SCTs requirement is effective from.
235 // 2015-07-01 00:00:00 UTC.
236 base::Time kDiverseSCTRequirementStartDate =
237 base::Time::FromInternalValue(13080182400000000);
238
239 for (const auto& it : verified_scts) {
240 if (it->timestamp < kDiverseSCTRequirementStartDate)
241 return false;
242 }
243
244 return true;
245 }
246
247 bool IsCertificateInWhitelist(const X509Certificate& cert,
248 const ct::EVCertsWhitelist* ev_whitelist) {
249 bool cert_in_ev_whitelist = false;
250 if (ev_whitelist && ev_whitelist->IsValid()) {
251 const SHA256HashValue fingerprint(
252 X509Certificate::CalculateFingerprint256(cert.os_cert_handle()));
253
254 std::string truncated_fp =
255 std::string(reinterpret_cast<const char*>(fingerprint.data), 8);
256 cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp);
257
258 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist",
259 cert_in_ev_whitelist);
260 }
261 return cert_in_ev_whitelist;
262 }
263
264 void CheckCTEVPolicyCompliance(X509Certificate* cert,
265 const ct::EVCertsWhitelist* ev_whitelist,
266 const ct::CTVerifyResult& ct_result,
267 ComplianceDetails* result) {
268 result->ct_presence_required = true;
269
270 if (!IsBuildTimely())
271 return;
272 result->build_timely = true;
273
274 if (ev_whitelist && ev_whitelist->IsValid())
275 result->whitelist_version = ev_whitelist->Version();
276
277 if (IsCertificateInWhitelist(*cert, ev_whitelist)) {
278 result->status = CT_IN_WHITELIST;
279 return;
280 }
281
282 if (!HasRequiredNumberOfSCTs(*cert, ct_result)) {
283 result->status = CT_NOT_COMPLIANT;
284 return;
285 }
286
287 if (AllSCTsPastDistinctSCTRequirementEnforcementDate(
288 ct_result.verified_scts) &&
289 !HasEnoughDiverseSCTs(ct_result.verified_scts)) {
290 result->status = CT_NOT_ENOUGH_DIVERSE_SCTS;
291 return;
292 }
293
294 result->status = CT_ENOUGH_SCTS;
295 }
296
297 } // namespace
298
299 bool CertPolicyEnforcer::DoesConformToCTEVPolicy(
300 X509Certificate* cert,
301 const ct::EVCertsWhitelist* ev_whitelist,
302 const ct::CTVerifyResult& ct_result,
303 const BoundNetLog& net_log) {
304 ComplianceDetails details;
305
306 CheckCTEVPolicyCompliance(cert, ev_whitelist, ct_result, &details);
307
308 NetLog::ParametersCallback net_log_callback =
309 base::Bind(&NetLogComplianceCheckResultCallback, base::Unretained(cert),
310 base::Unretained(&details));
311
312 net_log.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED,
313 net_log_callback);
314
315 if (!details.ct_presence_required)
316 return true;
317
318 if (!details.build_timely)
319 return false;
320
321 LogCTComplianceStatusToUMA(details.status, ev_whitelist);
322
323 if (details.status == CT_IN_WHITELIST || details.status == CT_ENOUGH_SCTS)
324 return true;
325
326 return false;
327 }
328
329 } // 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