| 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
|
|
|