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