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"
10 #include "base/build_time.h"
11 #include "base/callback_helpers.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/histogram.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"
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)
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 */;
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
);
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
)
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)
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.
99 bool has_partial_month
;
100 RoundedDownMonthDifference(cert
.valid_start(), cert
.valid_expiry(), &lifetime
,
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
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;
114 num_required_embedded_scts
= 2;
117 return num_embedded_scts
>= num_required_embedded_scts
;
120 enum CTComplianceStatus
{
121 CT_NOT_COMPLIANT
= 0,
127 const char* ComplianceStatusToString(CTComplianceStatus status
) {
129 case CT_NOT_COMPLIANT
:
130 return "NOT_COMPLIANT";
132 case CT_IN_WHITELIST
:
133 return "WHITELISTED";
136 return "ENOUGH_SCTS";
138 case CT_COMPLIANCE_MAX
:
145 enum EVWhitelistStatus
{
146 EV_WHITELIST_NOT_PRESENT
= 0,
147 EV_WHITELIST_INVALID
= 1,
148 EV_WHITELIST_VALID
= 2,
152 void LogCTComplianceStatusToUMA(CTComplianceStatus status
,
153 const ct::EVCertsWhitelist
* ev_whitelist
) {
154 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status
,
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
;
162 ev_whitelist_status
= EV_WHITELIST_INVALID
;
165 UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVWhitelistValidityForNonCompliantCert",
166 ev_whitelist_status
, EV_WHITELIST_MAX
);
170 struct ComplianceDetails
{
172 : ct_presence_required(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.
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 base::Value
* NetLogComplianceCheckResultCallback(X509Certificate
* cert
,
189 ComplianceDetails
* details
,
190 NetLog::LogLevel log_level
) {
191 base::DictionaryValue
* dict
= new base::DictionaryValue();
192 dict
->Set("certificate", NetLogX509CertificateCallback(cert
, log_level
));
193 dict
->SetBoolean("policy_enforcement_required",
194 details
->ct_presence_required
);
195 if (details
->ct_presence_required
) {
196 dict
->SetBoolean("build_timely", details
->build_timely
);
197 if (details
->build_timely
) {
198 dict
->SetString("ct_compliance_status",
199 ComplianceStatusToString(details
->status
));
200 if (details
->whitelist_version
.IsValid())
201 dict
->SetString("ev_whitelist_version",
202 details
->whitelist_version
.GetString());
208 bool IsCertificateInWhitelist(const X509Certificate
& cert
,
209 const ct::EVCertsWhitelist
* ev_whitelist
) {
210 bool cert_in_ev_whitelist
= false;
211 if (ev_whitelist
&& ev_whitelist
->IsValid()) {
212 const SHA256HashValue
fingerprint(
213 X509Certificate::CalculateFingerprint256(cert
.os_cert_handle()));
215 std::string truncated_fp
=
216 std::string(reinterpret_cast<const char*>(fingerprint
.data
), 8);
217 cert_in_ev_whitelist
= ev_whitelist
->ContainsCertificateHash(truncated_fp
);
219 UMA_HISTOGRAM_BOOLEAN("Net.SSL_EVCertificateInWhitelist",
220 cert_in_ev_whitelist
);
222 return cert_in_ev_whitelist
;
225 void CheckCTEVPolicyCompliance(X509Certificate
* cert
,
226 const ct::EVCertsWhitelist
* ev_whitelist
,
227 const ct::CTVerifyResult
& ct_result
,
228 ComplianceDetails
* result
) {
229 result
->ct_presence_required
= true;
231 if (!IsBuildTimely())
233 result
->build_timely
= true;
235 if (ev_whitelist
&& ev_whitelist
->IsValid())
236 result
->whitelist_version
= ev_whitelist
->Version();
238 if (IsCertificateInWhitelist(*cert
, ev_whitelist
)) {
239 result
->status
= CT_IN_WHITELIST
;
243 if (HasRequiredNumberOfSCTs(*cert
, ct_result
)) {
244 result
->status
= CT_ENOUGH_SCTS
;
248 result
->status
= CT_NOT_COMPLIANT
;
253 CertPolicyEnforcer::CertPolicyEnforcer(bool require_ct_for_ev
)
254 : require_ct_for_ev_(require_ct_for_ev
) {
257 CertPolicyEnforcer::~CertPolicyEnforcer() {
260 bool CertPolicyEnforcer::DoesConformToCTEVPolicy(
261 X509Certificate
* cert
,
262 const ct::EVCertsWhitelist
* ev_whitelist
,
263 const ct::CTVerifyResult
& ct_result
,
264 const BoundNetLog
& net_log
) {
265 ComplianceDetails details
;
267 if (require_ct_for_ev_
)
268 CheckCTEVPolicyCompliance(cert
, ev_whitelist
, ct_result
, &details
);
270 NetLog::ParametersCallback net_log_callback
=
271 base::Bind(&NetLogComplianceCheckResultCallback
, base::Unretained(cert
),
272 base::Unretained(&details
));
274 net_log
.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED
,
277 if (!details
.ct_presence_required
)
280 if (!details
.build_timely
)
283 LogCTComplianceStatusToUMA(details
.status
, ev_whitelist
);
285 if (details
.status
== CT_IN_WHITELIST
|| details
.status
== CT_ENOUGH_SCTS
)