OLD | NEW |
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/ct_policy_enforcer.h" | 5 #include "net/cert/ct_policy_enforcer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 12 matching lines...) Expand all Loading... |
23 #include "net/cert/ct_verify_result.h" | 23 #include "net/cert/ct_verify_result.h" |
24 #include "net/cert/signed_certificate_timestamp.h" | 24 #include "net/cert/signed_certificate_timestamp.h" |
25 #include "net/cert/x509_certificate.h" | 25 #include "net/cert/x509_certificate.h" |
26 #include "net/cert/x509_certificate_net_log_param.h" | 26 #include "net/cert/x509_certificate_net_log_param.h" |
27 #include "net/log/net_log.h" | 27 #include "net/log/net_log.h" |
28 | 28 |
29 namespace net { | 29 namespace net { |
30 | 30 |
31 namespace { | 31 namespace { |
32 | 32 |
33 bool IsEmbeddedSCT(const scoped_refptr<ct::SignedCertificateTimestamp>& sct) { | |
34 return sct->origin == ct::SignedCertificateTimestamp::SCT_EMBEDDED; | |
35 } | |
36 | |
37 // 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 |
38 // built-in security information (e.g. CT Logs) is fresh enough. | 34 // built-in security information (e.g. CT Logs) is fresh enough. |
39 // TODO(eranm): Move to base or net/base | 35 // TODO(eranm): Move to base or net/base |
40 bool IsBuildTimely() { | 36 bool IsBuildTimely() { |
41 const base::Time build_time = base::GetBuildTime(); | 37 const base::Time build_time = base::GetBuildTime(); |
42 // We consider built-in information to be timely for 10 weeks. | 38 // We consider built-in information to be timely for 10 weeks. |
43 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; | 39 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; |
44 } | 40 } |
45 | 41 |
46 bool IsGoogleIssuedSCT( | 42 bool IsGoogleIssuedSCT( |
(...skipping 24 matching lines...) Expand all Loading... |
71 uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 + | 67 uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 + |
72 (exploded_expiry.month - exploded_start.month); | 68 (exploded_expiry.month - exploded_start.month); |
73 if (exploded_expiry.day_of_month < exploded_start.day_of_month) | 69 if (exploded_expiry.day_of_month < exploded_start.day_of_month) |
74 --month_diff; | 70 --month_diff; |
75 else if (exploded_expiry.day_of_month == exploded_start.day_of_month) | 71 else if (exploded_expiry.day_of_month == exploded_start.day_of_month) |
76 *has_partial_month = false; | 72 *has_partial_month = false; |
77 | 73 |
78 *rounded_months_difference = month_diff; | 74 *rounded_months_difference = month_diff; |
79 } | 75 } |
80 | 76 |
| 77 bool CheckSCTPolicy(const X509Certificate& cert, |
| 78 const ct::SCTList& verified_scts) { |
| 79 const base::Time now = base::Time::Now(); |
| 80 const base::Time earliest_sct = base::Time::Max(); |
| 81 |
| 82 // Scan for the earliest SCT; this is used to determine whether or not to |
| 83 // enforce log diversity requirements, as well as to determine when to |
| 84 // compare log disqualification. It is OK to ignore the source of the SCT |
| 85 // here, because it is presumed that an OCSP/TLS-delivered SCT cannot |
| 86 // predate those delivered in the certificate, unless the CA submitted the |
| 87 // precert to additional logs that didn't appear in the certificate. If |
| 88 // the CA did, that is acceptable proof that the certificate was issued |
| 89 // at that time. |
| 90 for (const auto& sct : verified_scts) { |
| 91 base::Time unused; |
| 92 // Only allow still-valid logs to contribute to determining the issuance |
| 93 // date. |
| 94 if (IsLogDisqualified(sct->log_id, &unused)) |
| 95 continue; |
| 96 if (it->timestamp < earliest_sct) |
| 97 earliest_timestamp = it->timestamp; |
| 98 } |
| 99 |
| 100 enum { |
| 101 HAS_GOOGLE_SCT = (1 << 0), |
| 102 HAS_NON_GOOGLE_SCT = (1 << 1), |
| 103 HAS_DIVERSE_SCTS = (HAS_GOOGLE_SCT & HAS_NON_GOOGLE_SCT), |
| 104 }; |
| 105 |
| 106 struct SCTPolicyStatus { |
| 107 int diversity = 0; |
| 108 size_t qualifying_scts = 0; |
| 109 }; |
| 110 |
| 111 SCTPolicyStatus ocsp_status; |
| 112 SCTPolicyStatus tls_status; |
| 113 SCTPolicyStatus embedded_status; |
| 114 bool has_one_valid_embedded = false; |
| 115 |
| 116 for (const auto& sct : verified_scts) { |
| 117 base::Time disqualification_date; |
| 118 bool is_disqualified = |
| 119 IsLogDisqualified(sct->log_id, &disqualification_date); |
| 120 |
| 121 // For OCSP and TLS-delivered SCTs, only SCTs that are valid at the time |
| 122 // of check are accepted. |
| 123 if (sct->origin != SignedCertificateTimestamp::SCT_EMBEDDED && |
| 124 is_disqualified) |
| 125 continue; |
| 126 // TODO(rsleevi): To be truly consistent, we wouldn't count disqualified |
| 127 // SCTs towards the diversity requirement for precerts, requiring one |
| 128 // (valid) Google and one (valid) non-Google. If that was the case, this |
| 129 // would be rewritten as: |
| 130 // if (is_disqualified && |
| 131 // (sct->origin != SignedCertificateTimestamp::SCT_EMBEDDED || |
| 132 // earliest_sct >= disqualification_date || |
| 133 // sct->timestamp >= disqualification_date)) { |
| 134 // continue; |
| 135 // } |
| 136 SCTPolicyStatus* policy_status = nullptr; |
| 137 |
| 138 switch (sct->origin) { |
| 139 case SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION: |
| 140 tls_status.qualifying_scts++; |
| 141 policy_status = &tls_status; |
| 142 break; |
| 143 case SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE: |
| 144 ocsp_status.qualifying_scts++; |
| 145 policy_status = &ocsp_status; |
| 146 break; |
| 147 case SignedCertificateTimestamp::SCT_EMBEDDED: |
| 148 if (is_disqualified && earliest_sct < disqualification_date && |
| 149 sct->timestamp < disqualification_date) |
| 150 embedded_status.qualifying_scts++; |
| 151 policy_status = &embedded_status; |
| 152 break; |
| 153 default: |
| 154 NOTREACHED(); |
| 155 break; |
| 156 } |
| 157 if (policy_status->diversity != HAS_DIVERSE_SCTS) { |
| 158 policy_status->diversity &= IsLogOperatedByGoogle(sct->log_id) |
| 159 ? HAS_GOOGLE_SCT |
| 160 : HAS_NON_GOOGLE_SCT; |
| 161 } |
| 162 } |
| 163 if (tls_status.qualifying_scts >= 2 && |
| 164 tls_status.diversity == HAS_DIVERSE_SCTS || |
| 165 ocsp_status.qualifying_scts >= 2 && |
| 166 ocsp_status.diversity == HAS_DIVERSE_SCTS) |
| 167 return true; |
| 168 |
| 169 size_t num_expected_scts = 0; |
| 170 if (embedded_status.qualifying_scts >= num_expected_scts && |
| 171 embedded_status.diversity == HAS_DIVERSE_SCTS) { |
| 172 return true; |
| 173 } |
| 174 |
| 175 return false; |
| 176 } |
| 177 |
81 bool HasRequiredNumberOfSCTs(const X509Certificate& cert, | 178 bool HasRequiredNumberOfSCTs(const X509Certificate& cert, |
82 const ct::SCTList& verified_scts) { | 179 const ct::SCTList& verified_scts) { |
| 180 const base::Time now = base::Time::Now(); |
| 181 |
83 size_t num_valid_scts = verified_scts.size(); | 182 size_t num_valid_scts = verified_scts.size(); |
84 size_t num_embedded_scts = base::checked_cast<size_t>( | 183 size_t num_embedded_scts = base::checked_cast<size_t>(std::count_if( |
85 std::count_if(verified_scts.begin(), verified_scts.end(), IsEmbeddedSCT)); | 184 verified_scts.begin(), verified_scts.end(), IsQualifyingEmbeddedSCT)); |
86 | 185 |
87 size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts; | 186 size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts; |
88 // If at least two valid SCTs were delivered by means other than embedding | 187 // If at least two valid SCTs were delivered by means other than embedding |
89 // (i.e. in a TLS extension or OCSP), then the certificate conforms to bullet | 188 // (i.e. in a TLS extension or OCSP), then the certificate conforms to bullet |
90 // number 3 of the "Qualifying Certificate" section of the CT/EV policy. | 189 // number 3 of the "Qualifying Certificate" section of the CT/EV policy. |
91 if (num_non_embedded_scts >= 2) | 190 if (num_non_embedded_scts >= 2) |
92 return true; | 191 return true; |
93 | 192 |
94 if (cert.valid_start().is_null() || cert.valid_expiry().is_null() || | 193 if (cert.valid_start().is_null() || cert.valid_expiry().is_null() || |
95 cert.valid_start().is_max() || cert.valid_expiry().is_max()) { | 194 cert.valid_start().is_max() || cert.valid_expiry().is_max()) { |
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
372 | 471 |
373 if (!details.build_timely) | 472 if (!details.build_timely) |
374 return ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; | 473 return ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; |
375 | 474 |
376 LogEVPolicyComplianceToUMA(details.status, ev_whitelist); | 475 LogEVPolicyComplianceToUMA(details.status, ev_whitelist); |
377 | 476 |
378 return details.status; | 477 return details.status; |
379 } | 478 } |
380 | 479 |
381 } // namespace net | 480 } // namespace net |
OLD | NEW |