tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CTPolicyEnforcer.cpp (6803B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CTPolicyEnforcer.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozpkix/Time.h"
     11 #include <set>
     12 #include <stdint.h>
     13 
     14 namespace mozilla {
     15 namespace ct {
     16 
     17 using namespace mozilla::pkix;
     18 
     19 // Returns the number of embedded SCTs required to be present in a certificate.
     20 // For certificates with a lifetime of less than or equal to 180 days, only 2
     21 // embedded SCTs are required. Otherwise 3 are required.
     22 MOZ_RUNINIT const Duration ONE_HUNDRED_AND_EIGHTY_DAYS =
     23    Duration(180 * Time::ONE_DAY_IN_SECONDS);
     24 size_t GetRequiredEmbeddedSctsCount(Duration certLifetime) {
     25  // pkix::Duration doesn't define operator<=, hence phrasing this comparison
     26  // in an awkward way
     27  return ONE_HUNDRED_AND_EIGHTY_DAYS < certLifetime ? 3 : 2;
     28 }
     29 
     30 // Calculates the effective issuance time of connection's certificate using
     31 // the SCTs present on the connection (we can't rely on notBefore validity
     32 // field of the certificate since it can be backdated).
     33 // Used to determine whether to accept SCTs issued by past qualified logs.
     34 // The effective issuance time is defined as the earliest of all SCTs,
     35 // rather than the latest of embedded SCTs, in order to give CAs the benefit
     36 // of the doubt in the event a log is revoked in the midst of processing
     37 // a precertificate and issuing the certificate.
     38 // It is acceptable to ignore the origin of the SCTs because SCTs
     39 // delivered via OCSP/TLS extension will cover the full certificate,
     40 // which necessarily will exist only after the precertificate
     41 // has been logged and the actual certificate issued.
     42 uint64_t GetEffectiveCertIssuanceTime(const VerifiedSCTList& verifiedScts) {
     43  uint64_t result = UINT64_MAX;
     44  for (const VerifiedSCT& verifiedSct : verifiedScts) {
     45    if (verifiedSct.logState == CTLogState::Admissible) {
     46      result = std::min(result, verifiedSct.sct.timestamp);
     47    }
     48  }
     49  return result;
     50 }
     51 
     52 // Checks if the log that issued the given SCT is "once or currently qualified"
     53 // (i.e. was qualified at the time of the certificate issuance). In addition,
     54 // makes sure the SCT is before the retirement timestamp.
     55 bool LogWasQualifiedForSct(const VerifiedSCT& verifiedSct,
     56                           uint64_t certIssuanceTime) {
     57  switch (verifiedSct.logState) {
     58    case CTLogState::Admissible:
     59      return true;
     60    case CTLogState::Retired: {
     61      uint64_t logRetirementTime = verifiedSct.logTimestamp;
     62      return certIssuanceTime < logRetirementTime &&
     63             verifiedSct.sct.timestamp < logRetirementTime;
     64    }
     65  }
     66  MOZ_ASSERT_UNREACHABLE("verifiedSct.logState must be Admissible or Retired");
     67  return false;
     68 }
     69 
     70 // Qualification for embedded SCTs:
     71 // There must be at least one embedded SCT from a log that was Admissible (i.e.
     72 // Qualified, Usable, or ReadOnly) at the time of the check.
     73 // There must be at least N embedded SCTs from distinct logs that were
     74 // Admissible or Retired at the time of the check, where N depends on the
     75 // lifetime of the certificate. If the certificate lifetime is less than or
     76 // equal to 180 days, N is 2. Otherwise, N is 3.
     77 // Among these SCTs, at least two must be issued from distinct log operators.
     78 CTPolicyCompliance EmbeddedSCTsCompliant(const VerifiedSCTList& verifiedScts,
     79                                         uint64_t certIssuanceTime,
     80                                         Duration certLifetime) {
     81  size_t admissibleCount = 0;
     82  size_t admissibleOrRetiredCount = 0;
     83  std::set<CTLogOperatorId> logOperators;
     84  std::set<Buffer> logIds;
     85  for (const auto& verifiedSct : verifiedScts) {
     86    if (verifiedSct.origin != SCTOrigin::Embedded) {
     87      continue;
     88    }
     89    if (verifiedSct.logState != CTLogState::Admissible &&
     90        !LogWasQualifiedForSct(verifiedSct, certIssuanceTime)) {
     91      continue;
     92    }
     93    // SCTs from tiled logs "MUST" have a valid leaf index extension.
     94    if (verifiedSct.logFormat == CTLogFormat::Tiled &&
     95        verifiedSct.sct.leafIndex.isNothing()) {
     96      continue;
     97    }
     98    // Note that a single SCT can count for both the "from a log that was
     99    // admissible" case and the "from a log that was admissible or retired"
    100    // case.
    101    if (verifiedSct.logState == CTLogState::Admissible) {
    102      admissibleCount++;
    103    }
    104    if (LogWasQualifiedForSct(verifiedSct, certIssuanceTime)) {
    105      admissibleOrRetiredCount++;
    106      logIds.insert(verifiedSct.sct.logId);
    107    }
    108    logOperators.insert(verifiedSct.logOperatorId);
    109  }
    110 
    111  size_t requiredEmbeddedScts = GetRequiredEmbeddedSctsCount(certLifetime);
    112  if (admissibleCount < 1 || admissibleOrRetiredCount < requiredEmbeddedScts) {
    113    return CTPolicyCompliance::NotEnoughScts;
    114  }
    115  if (logIds.size() < requiredEmbeddedScts || logOperators.size() < 2) {
    116    return CTPolicyCompliance::NotDiverseScts;
    117  }
    118  return CTPolicyCompliance::Compliant;
    119 }
    120 
    121 // Qualification for non-embedded SCTs (i.e. SCTs delivered via TLS handshake
    122 // or OCSP response):
    123 // There must be at least two SCTs from logs that were Admissible (i.e.
    124 // Qualified, Usable, or ReadOnly) at the time of the check. Among these SCTs,
    125 // at least two must be issued from distinct log operators.
    126 CTPolicyCompliance NonEmbeddedSCTsCompliant(
    127    const VerifiedSCTList& verifiedScts) {
    128  size_t admissibleCount = 0;
    129  std::set<CTLogOperatorId> logOperators;
    130  std::set<Buffer> logIds;
    131  for (const auto& verifiedSct : verifiedScts) {
    132    if (verifiedSct.origin == SCTOrigin::Embedded) {
    133      continue;
    134    }
    135    if (verifiedSct.logState != CTLogState::Admissible) {
    136      continue;
    137    }
    138    // SCTs from tiled logs "MUST" have a valid leaf index extension.
    139    if (verifiedSct.logFormat == CTLogFormat::Tiled &&
    140        verifiedSct.sct.leafIndex.isNothing()) {
    141      continue;
    142    }
    143    admissibleCount++;
    144    logIds.insert(verifiedSct.sct.logId);
    145    logOperators.insert(verifiedSct.logOperatorId);
    146  }
    147 
    148  if (admissibleCount < 2) {
    149    return CTPolicyCompliance::NotEnoughScts;
    150  }
    151  if (logIds.size() < 2 || logOperators.size() < 2) {
    152    return CTPolicyCompliance::NotDiverseScts;
    153  }
    154  return CTPolicyCompliance::Compliant;
    155 }
    156 
    157 CTPolicyCompliance CheckCTPolicyCompliance(const VerifiedSCTList& verifiedScts,
    158                                           Duration certLifetime) {
    159  if (NonEmbeddedSCTsCompliant(verifiedScts) == CTPolicyCompliance::Compliant) {
    160    return CTPolicyCompliance::Compliant;
    161  }
    162 
    163  uint64_t certIssuanceTime = GetEffectiveCertIssuanceTime(verifiedScts);
    164  return EmbeddedSCTsCompliant(verifiedScts, certIssuanceTime, certLifetime);
    165 }
    166 
    167 }  // namespace ct
    168 }  // namespace mozilla