Index: net/cert/ct_policy_enforcer.cc |
diff --git a/net/cert/ct_policy_enforcer.cc b/net/cert/ct_policy_enforcer.cc |
index e036087c5cab04ec9b16e33d1f25cf2f17118d50..841e97646ece7eb8b0635a0bea0ab3ff7e9b7743 100644 |
--- a/net/cert/ct_policy_enforcer.cc |
+++ b/net/cert/ct_policy_enforcer.cc |
@@ -30,10 +30,6 @@ namespace net { |
namespace { |
-bool IsEmbeddedSCT(const scoped_refptr<ct::SignedCertificateTimestamp>& sct) { |
- return sct->origin == ct::SignedCertificateTimestamp::SCT_EMBEDDED; |
-} |
- |
// Returns true if the current build is recent enough to ensure that |
// built-in security information (e.g. CT Logs) is fresh enough. |
// TODO(eranm): Move to base or net/base |
@@ -78,11 +74,114 @@ void RoundedDownMonthDifference(const base::Time& start, |
*rounded_months_difference = month_diff; |
} |
+bool CheckSCTPolicy(const X509Certificate& cert, |
+ const ct::SCTList& verified_scts) { |
+ const base::Time now = base::Time::Now(); |
+ const base::Time earliest_sct = base::Time::Max(); |
+ |
+ // Scan for the earliest SCT; this is used to determine whether or not to |
+ // enforce log diversity requirements, as well as to determine when to |
+ // compare log disqualification. It is OK to ignore the source of the SCT |
+ // here, because it is presumed that an OCSP/TLS-delivered SCT cannot |
+ // predate those delivered in the certificate, unless the CA submitted the |
+ // precert to additional logs that didn't appear in the certificate. If |
+ // the CA did, that is acceptable proof that the certificate was issued |
+ // at that time. |
+ for (const auto& sct : verified_scts) { |
+ base::Time unused; |
+ // Only allow still-valid logs to contribute to determining the issuance |
+ // date. |
+ if (IsLogDisqualified(sct->log_id, &unused)) |
+ continue; |
+ if (it->timestamp < earliest_sct) |
+ earliest_timestamp = it->timestamp; |
+ } |
+ |
+ enum { |
+ HAS_GOOGLE_SCT = (1 << 0), |
+ HAS_NON_GOOGLE_SCT = (1 << 1), |
+ HAS_DIVERSE_SCTS = (HAS_GOOGLE_SCT & HAS_NON_GOOGLE_SCT), |
+ }; |
+ |
+ struct SCTPolicyStatus { |
+ int diversity = 0; |
+ size_t qualifying_scts = 0; |
+ }; |
+ |
+ SCTPolicyStatus ocsp_status; |
+ SCTPolicyStatus tls_status; |
+ SCTPolicyStatus embedded_status; |
+ bool has_one_valid_embedded = false; |
+ |
+ for (const auto& sct : verified_scts) { |
+ base::Time disqualification_date; |
+ bool is_disqualified = |
+ IsLogDisqualified(sct->log_id, &disqualification_date); |
+ |
+ // For OCSP and TLS-delivered SCTs, only SCTs that are valid at the time |
+ // of check are accepted. |
+ if (sct->origin != SignedCertificateTimestamp::SCT_EMBEDDED && |
+ is_disqualified) |
+ continue; |
+ // TODO(rsleevi): To be truly consistent, we wouldn't count disqualified |
+ // SCTs towards the diversity requirement for precerts, requiring one |
+ // (valid) Google and one (valid) non-Google. If that was the case, this |
+ // would be rewritten as: |
+ // if (is_disqualified && |
+ // (sct->origin != SignedCertificateTimestamp::SCT_EMBEDDED || |
+ // earliest_sct >= disqualification_date || |
+ // sct->timestamp >= disqualification_date)) { |
+ // continue; |
+ // } |
+ SCTPolicyStatus* policy_status = nullptr; |
+ |
+ switch (sct->origin) { |
+ case SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION: |
+ tls_status.qualifying_scts++; |
+ policy_status = &tls_status; |
+ break; |
+ case SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE: |
+ ocsp_status.qualifying_scts++; |
+ policy_status = &ocsp_status; |
+ break; |
+ case SignedCertificateTimestamp::SCT_EMBEDDED: |
+ if (is_disqualified && earliest_sct < disqualification_date && |
+ sct->timestamp < disqualification_date) |
+ embedded_status.qualifying_scts++; |
+ policy_status = &embedded_status; |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ if (policy_status->diversity != HAS_DIVERSE_SCTS) { |
+ policy_status->diversity &= IsLogOperatedByGoogle(sct->log_id) |
+ ? HAS_GOOGLE_SCT |
+ : HAS_NON_GOOGLE_SCT; |
+ } |
+ } |
+ if (tls_status.qualifying_scts >= 2 && |
+ tls_status.diversity == HAS_DIVERSE_SCTS || |
+ ocsp_status.qualifying_scts >= 2 && |
+ ocsp_status.diversity == HAS_DIVERSE_SCTS) |
+ return true; |
+ |
+ size_t num_expected_scts = 0; |
+ if (embedded_status.qualifying_scts >= num_expected_scts && |
+ embedded_status.diversity == HAS_DIVERSE_SCTS) { |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
bool HasRequiredNumberOfSCTs(const X509Certificate& cert, |
const ct::SCTList& verified_scts) { |
+ const base::Time now = base::Time::Now(); |
+ |
size_t num_valid_scts = verified_scts.size(); |
- size_t num_embedded_scts = base::checked_cast<size_t>( |
- std::count_if(verified_scts.begin(), verified_scts.end(), IsEmbeddedSCT)); |
+ size_t num_embedded_scts = base::checked_cast<size_t>(std::count_if( |
+ verified_scts.begin(), verified_scts.end(), IsQualifyingEmbeddedSCT)); |
size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts; |
// If at least two valid SCTs were delivered by means other than embedding |