Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / cert / cert_policy_enforcer.cc
blob070f8691ee622ee941fadf06de6a503947bfa89c
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.
5 #include "net/cert/cert_policy_enforcer.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/build_time.h"
11 #include "base/callback_helpers.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/numerics/safe_conversions.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/values.h"
17 #include "base/version.h"
18 #include "net/cert/ct_ev_whitelist.h"
19 #include "net/cert/ct_verify_result.h"
20 #include "net/cert/signed_certificate_timestamp.h"
21 #include "net/cert/x509_certificate.h"
22 #include "net/cert/x509_certificate_net_log_param.h"
23 #include "net/log/net_log.h"
25 namespace net {
27 namespace {
29 bool IsEmbeddedSCT(const scoped_refptr<ct::SignedCertificateTimestamp>& sct) {
30 return sct->origin == ct::SignedCertificateTimestamp::SCT_EMBEDDED;
33 // Returns true if the current build is recent enough to ensure that
34 // built-in security information (e.g. CT Logs) is fresh enough.
35 // TODO(eranm): Move to base or net/base
36 bool IsBuildTimely() {
37 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD)
38 return true;
39 #else
40 const base::Time build_time = base::GetBuildTime();
41 // We consider built-in information to be timely for 10 weeks.
42 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */;
43 #endif
46 // Returns a rounded-down months difference of |start| and |end|,
47 // together with an indication of whether the last month was
48 // a full month, because the range starts specified in the policy
49 // are not consistent in terms of including the range start value.
50 void RoundedDownMonthDifference(const base::Time& start,
51 const base::Time& end,
52 size_t* rounded_months_difference,
53 bool* has_partial_month) {
54 DCHECK(rounded_months_difference);
55 DCHECK(has_partial_month);
56 base::Time::Exploded exploded_start;
57 base::Time::Exploded exploded_expiry;
58 start.UTCExplode(&exploded_start);
59 end.UTCExplode(&exploded_expiry);
60 if (end < start) {
61 *rounded_months_difference = 0;
62 *has_partial_month = false;
65 *has_partial_month = true;
66 uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 +
67 (exploded_expiry.month - exploded_start.month);
68 if (exploded_expiry.day_of_month < exploded_start.day_of_month)
69 --month_diff;
70 else if (exploded_expiry.day_of_month == exploded_start.day_of_month)
71 *has_partial_month = false;
73 *rounded_months_difference = month_diff;
76 bool HasRequiredNumberOfSCTs(const X509Certificate& cert,
77 const ct::CTVerifyResult& ct_result) {
78 // TODO(eranm): Count the number of *independent* SCTs once the information
79 // about log operators is available, crbug.com/425174
80 size_t num_valid_scts = ct_result.verified_scts.size();
81 size_t num_embedded_scts =
82 std::count_if(ct_result.verified_scts.begin(),
83 ct_result.verified_scts.end(), IsEmbeddedSCT);
85 size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts;
86 // If at least two valid SCTs were delivered by means other than embedding
87 // (i.e. in a TLS extension or OCSP), then the certificate conforms to bullet
88 // number 3 of the "Qualifying Certificate" section of the CT/EV policy.
89 if (num_non_embedded_scts >= 2)
90 return true;
92 if (cert.valid_start().is_null() || cert.valid_expiry().is_null() ||
93 cert.valid_start().is_max() || cert.valid_expiry().is_max()) {
94 // Will not be able to calculate the certificate's validity period.
95 return false;
98 size_t lifetime;
99 bool has_partial_month;
100 RoundedDownMonthDifference(cert.valid_start(), cert.valid_expiry(), &lifetime,
101 &has_partial_month);
103 // For embedded SCTs, if the certificate has the number of SCTs specified in
104 // table 1 of the "Qualifying Certificate" section of the CT/EV policy, then
105 // it qualifies.
106 size_t num_required_embedded_scts;
107 if (lifetime > 39 || (lifetime == 39 && has_partial_month)) {
108 num_required_embedded_scts = 5;
109 } else if (lifetime > 27 || (lifetime == 27 && has_partial_month)) {
110 num_required_embedded_scts = 4;
111 } else if (lifetime >= 15) {
112 num_required_embedded_scts = 3;
113 } else {
114 num_required_embedded_scts = 2;
117 return num_embedded_scts >= num_required_embedded_scts;
120 enum CTComplianceStatus {
121 CT_NOT_COMPLIANT = 0,
122 CT_IN_WHITELIST = 1,
123 CT_ENOUGH_SCTS = 2,
124 CT_COMPLIANCE_MAX,
127 const char* ComplianceStatusToString(CTComplianceStatus status) {
128 switch (status) {
129 case CT_NOT_COMPLIANT:
130 return "NOT_COMPLIANT";
131 break;
132 case CT_IN_WHITELIST:
133 return "WHITELISTED";
134 break;
135 case CT_ENOUGH_SCTS:
136 return "ENOUGH_SCTS";
137 break;
138 case CT_COMPLIANCE_MAX:
139 break;
142 return "unknown";
145 enum EVWhitelistStatus {
146 EV_WHITELIST_NOT_PRESENT = 0,
147 EV_WHITELIST_INVALID = 1,
148 EV_WHITELIST_VALID = 2,
149 EV_WHITELIST_MAX,
152 void LogCTComplianceStatusToUMA(CTComplianceStatus status,
153 const ct::EVCertsWhitelist* ev_whitelist) {
154 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status,
155 CT_COMPLIANCE_MAX);
156 if (status == CT_NOT_COMPLIANT) {
157 EVWhitelistStatus ev_whitelist_status = EV_WHITELIST_NOT_PRESENT;
158 if (ev_whitelist != NULL) {
159 if (ev_whitelist->IsValid())
160 ev_whitelist_status = EV_WHITELIST_VALID;
161 else
162 ev_whitelist_status = EV_WHITELIST_INVALID;
165 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVWhitelistValidityForNonCompliantCert",
166 ev_whitelist_status, EV_WHITELIST_MAX);
170 struct ComplianceDetails {
171 ComplianceDetails()
172 : ct_presence_required(false),
173 build_timely(false),
174 status(CT_NOT_COMPLIANT) {}
176 // Whether enforcement of the policy was required or not.
177 bool ct_presence_required;
178 // Whether the build is not older than 10 weeks. The value is meaningful only
179 // if |ct_presence_required| is true.
180 bool build_timely;
181 // Compliance status - meaningful only if |ct_presence_required| and
182 // |build_timely| are true.
183 CTComplianceStatus status;
184 // EV whitelist version.
185 base::Version whitelist_version;
188 scoped_ptr<base::Value> NetLogComplianceCheckResultCallback(
189 X509Certificate* cert,
190 ComplianceDetails* details,
191 NetLogCaptureMode capture_mode) {
192 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
193 dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode));
194 dict->SetBoolean("policy_enforcement_required",
195 details->ct_presence_required);
196 if (details->ct_presence_required) {
197 dict->SetBoolean("build_timely", details->build_timely);
198 if (details->build_timely) {
199 dict->SetString("ct_compliance_status",
200 ComplianceStatusToString(details->status));
201 if (details->whitelist_version.IsValid())
202 dict->SetString("ev_whitelist_version",
203 details->whitelist_version.GetString());
206 return dict.Pass();
209 bool IsCertificateInWhitelist(const X509Certificate& cert,
210 const ct::EVCertsWhitelist* ev_whitelist) {
211 bool cert_in_ev_whitelist = false;
212 if (ev_whitelist && ev_whitelist->IsValid()) {
213 const SHA256HashValue fingerprint(
214 X509Certificate::CalculateFingerprint256(cert.os_cert_handle()));
216 std::string truncated_fp =
217 std::string(reinterpret_cast<const char*>(fingerprint.data), 8);
218 cert_in_ev_whitelist = ev_whitelist->ContainsCertificateHash(truncated_fp);
220 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist",
221 cert_in_ev_whitelist);
223 return cert_in_ev_whitelist;
226 void CheckCTEVPolicyCompliance(X509Certificate* cert,
227 const ct::EVCertsWhitelist* ev_whitelist,
228 const ct::CTVerifyResult& ct_result,
229 ComplianceDetails* result) {
230 result->ct_presence_required = true;
232 if (!IsBuildTimely())
233 return;
234 result->build_timely = true;
236 if (ev_whitelist && ev_whitelist->IsValid())
237 result->whitelist_version = ev_whitelist->Version();
239 if (IsCertificateInWhitelist(*cert, ev_whitelist)) {
240 result->status = CT_IN_WHITELIST;
241 return;
244 if (HasRequiredNumberOfSCTs(*cert, ct_result)) {
245 result->status = CT_ENOUGH_SCTS;
246 return;
249 result->status = CT_NOT_COMPLIANT;
252 } // namespace
254 bool CertPolicyEnforcer::DoesConformToCTEVPolicy(
255 X509Certificate* cert,
256 const ct::EVCertsWhitelist* ev_whitelist,
257 const ct::CTVerifyResult& ct_result,
258 const BoundNetLog& net_log) {
259 ComplianceDetails details;
261 CheckCTEVPolicyCompliance(cert, ev_whitelist, ct_result, &details);
263 NetLog::ParametersCallback net_log_callback =
264 base::Bind(&NetLogComplianceCheckResultCallback, base::Unretained(cert),
265 base::Unretained(&details));
267 net_log.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED,
268 net_log_callback);
270 if (!details.ct_presence_required)
271 return true;
273 if (!details.build_timely)
274 return false;
276 LogCTComplianceStatusToUMA(details.status, ev_whitelist);
278 if (details.status == CT_IN_WHITELIST || details.status == CT_ENOUGH_SCTS)
279 return true;
281 return false;
284 } // namespace net