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/. */
7 #include "CTPolicyEnforcer.h"
16 using namespace mozilla::pkix
;
18 // Returns the number of embedded SCTs required to be present on a
19 // certificate for Qualification Case #2 (embedded SCTs).
20 static size_t GetRequiredEmbeddedSctsCount(
21 size_t certLifetimeInFullCalendarMonths
) {
22 // "there are Embedded SCTs from AT LEAST N+1 once or currently qualified
23 // logs, where N is the lifetime of the certificate in years (normally
24 // rounding up, but rounding down when up to 3 months over), and must be
26 return 1 + (certLifetimeInFullCalendarMonths
+ 9) / 12;
29 // Whether a valid embedded SCT is present in the list.
30 static bool HasValidEmbeddedSct(const VerifiedSCTList
& verifiedScts
) {
31 for (const VerifiedSCT
& verifiedSct
: verifiedScts
) {
32 if (verifiedSct
.status
== VerifiedSCT::Status::Valid
&&
33 verifiedSct
.origin
== VerifiedSCT::Origin::Embedded
) {
40 // Whether a valid non-embedded SCT is present in the list.
41 static bool HasValidNonEmbeddedSct(const VerifiedSCTList
& verifiedScts
) {
42 for (const VerifiedSCT
& verifiedSct
: verifiedScts
) {
43 if (verifiedSct
.status
== VerifiedSCT::Status::Valid
&&
44 (verifiedSct
.origin
== VerifiedSCT::Origin::TLSExtension
||
45 verifiedSct
.origin
== VerifiedSCT::Origin::OCSPResponse
)) {
52 // Given a list of verified SCTs, counts the number of distinct CA-independent
53 // log operators running the CT logs that issued the SCTs which satisfy
54 // the provided boolean predicate.
55 template <typename SelectFunc
>
56 void CountIndependentLogOperatorsForSelectedScts(
57 const VerifiedSCTList
& verifiedScts
,
58 const CTLogOperatorList
& dependentOperators
, size_t& count
,
59 SelectFunc selected
) {
60 CTLogOperatorList operatorIds
;
61 for (const VerifiedSCT
& verifiedSct
: verifiedScts
) {
62 CTLogOperatorId sctLogOperatorId
= verifiedSct
.logOperatorId
;
63 // Check if |sctLogOperatorId| is CA-dependent.
64 bool isDependentOperator
= false;
65 for (CTLogOperatorId dependentOperator
: dependentOperators
) {
66 if (sctLogOperatorId
== dependentOperator
) {
67 isDependentOperator
= true;
71 if (isDependentOperator
|| !selected(verifiedSct
)) {
74 // Check if |sctLogOperatorId| is in |operatorIds|...
75 bool alreadyAdded
= false;
76 for (CTLogOperatorId id
: operatorIds
) {
77 if (id
== sctLogOperatorId
) {
82 // ...and if not, add it.
84 operatorIds
.push_back(sctLogOperatorId
);
87 count
= operatorIds
.size();
90 // Given a list of verified SCTs, counts the number of distinct CT logs
91 // that issued the SCTs that satisfy the |selected| predicate.
92 template <typename SelectFunc
>
93 static void CountLogsForSelectedScts(const VerifiedSCTList
& verifiedScts
,
94 size_t& count
, SelectFunc selected
) {
95 // Keep pointers to log ids (of type Buffer) from |verifiedScts| to save on
96 // memory allocations.
97 std::vector
<const Buffer
*> logIds
;
98 for (const VerifiedSCT
& verifiedSct
: verifiedScts
) {
99 if (!selected(verifiedSct
)) {
103 const Buffer
* sctLogId
= &verifiedSct
.sct
.logId
;
104 // Check if |sctLogId| points to data already in |logIds|...
105 bool alreadyAdded
= false;
106 for (const Buffer
* logId
: logIds
) {
107 if (*logId
== *sctLogId
) {
112 // ...and if not, add it.
114 logIds
.push_back(sctLogId
);
117 count
= logIds
.size();
120 // Calculates the effective issuance time of connection's certificate using
121 // the SCTs present on the connection (we can't rely on notBefore validity
122 // field of the certificate since it can be backdated).
123 // Used to determine whether to accept SCTs issued by past qualified logs.
124 // The effective issuance time is defined as the earliest of all SCTs,
125 // rather than the latest of embedded SCTs, in order to give CAs the benefit
126 // of the doubt in the event a log is revoked in the midst of processing
127 // a precertificate and issuing the certificate.
128 // It is acceptable to ignore the origin of the SCTs because SCTs
129 // delivered via OCSP/TLS extension will cover the full certificate,
130 // which necessarily will exist only after the precertificate
131 // has been logged and the actual certificate issued.
132 static uint64_t GetEffectiveCertIssuanceTime(
133 const VerifiedSCTList
& verifiedScts
) {
134 uint64_t result
= UINT64_MAX
;
135 for (const VerifiedSCT
& verifiedSct
: verifiedScts
) {
136 if (verifiedSct
.status
== VerifiedSCT::Status::Valid
) {
137 result
= std::min(result
, verifiedSct
.sct
.timestamp
);
143 // Checks if the log that issued the given SCT is "once or currently qualified"
144 // (i.e. was qualified at the time of the certificate issuance). In addition,
145 // makes sure the SCT is before the disqualification.
146 static bool LogWasQualifiedForSct(const VerifiedSCT
& verifiedSct
,
147 uint64_t certIssuanceTime
) {
148 if (verifiedSct
.status
== VerifiedSCT::Status::Valid
) {
151 if (verifiedSct
.status
== VerifiedSCT::Status::ValidFromDisqualifiedLog
) {
152 uint64_t logDisqualificationTime
= verifiedSct
.logDisqualificationTime
;
153 return certIssuanceTime
< logDisqualificationTime
&&
154 verifiedSct
.sct
.timestamp
< logDisqualificationTime
;
159 // "A certificate is CT Qualified if it is presented with at least two SCTs
160 // from once or currently qualified logs run by a minimum of two entities
161 // independent of the CA and of each other."
162 // By the preexisting certificate exception provision (not currently
163 // implemented), certificates "are CT Qualified if they are presented with SCTs
164 // from once or currently qualified logs run by a minimum of one entity
165 // independent of the CA."
166 static void CheckOperatorDiversityCompliance(
167 const VerifiedSCTList
& verifiedScts
, uint64_t certIssuanceTime
,
168 const CTLogOperatorList
& dependentOperators
, bool& compliant
) {
169 size_t independentOperatorsCount
;
170 CountIndependentLogOperatorsForSelectedScts(
171 verifiedScts
, dependentOperators
, independentOperatorsCount
,
172 [certIssuanceTime
](const VerifiedSCT
& verifiedSct
) -> bool {
173 return LogWasQualifiedForSct(verifiedSct
, certIssuanceTime
);
175 // Having at least 2 operators implies we have at least 2 SCTs.
176 // For the preexisting certificate exception provision (1 operator) we will
177 // need to include an additional SCTs count check using
178 // CountLogsForSelectedScts(verifiedScts, sctsCount,
179 // [certIssuanceTime](const VerifiedSCT& verifiedSct)->bool {
180 // return LogWasQualifiedForSct(verifiedSct, certIssuanceTime);
182 compliant
= independentOperatorsCount
>= 2;
185 // Qualification Case #1 (non-embedded SCTs) - the following must hold:
186 // a. An SCT from a log qualified at the time of check is presented via the
187 // TLS extension OR is embedded within a stapled OCSP response;
189 // b. There are at least two SCTs from logs qualified at the time of check,
190 // presented via any method.
191 static void CheckNonEmbeddedCompliance(const VerifiedSCTList
& verifiedScts
,
193 if (!HasValidNonEmbeddedSct(verifiedScts
)) {
198 size_t validSctsCount
;
199 CountLogsForSelectedScts(
200 verifiedScts
, validSctsCount
, [](const VerifiedSCT
& verifiedSct
) -> bool {
201 return verifiedSct
.status
== VerifiedSCT::Status::Valid
;
204 compliant
= validSctsCount
>= 2;
207 // Qualification Case #2 (embedded SCTs) - the following must hold:
208 // a. An Embedded SCT from a log qualified at the time of check is presented;
210 // b. There are Embedded SCTs from AT LEAST N + 1 once or currently qualified
211 // logs, where N is the lifetime of the certificate in years (normally
212 // rounding up, but rounding down when up to 3 months over), and must be
214 static void CheckEmbeddedCompliance(const VerifiedSCTList
& verifiedScts
,
215 size_t certLifetimeInCalendarMonths
,
216 uint64_t certIssuanceTime
,
218 if (!HasValidEmbeddedSct(verifiedScts
)) {
223 // Count the compliant embedded SCTs. Only a single SCT from each log
224 // is accepted. Note that a given log might return several different SCTs
225 // for the same precertificate (it is permitted, but advised against).
226 size_t embeddedSctsCount
;
227 CountLogsForSelectedScts(
228 verifiedScts
, embeddedSctsCount
,
229 [certIssuanceTime
](const VerifiedSCT
& verifiedSct
) -> bool {
230 return verifiedSct
.origin
== VerifiedSCT::Origin::Embedded
&&
231 LogWasQualifiedForSct(verifiedSct
, certIssuanceTime
);
234 size_t requiredSctsCount
=
235 GetRequiredEmbeddedSctsCount(certLifetimeInCalendarMonths
);
237 compliant
= embeddedSctsCount
>= requiredSctsCount
;
240 void CTPolicyEnforcer::CheckCompliance(
241 const VerifiedSCTList
& verifiedScts
, size_t certLifetimeInCalendarMonths
,
242 const CTLogOperatorList
& dependentOperators
,
243 CTPolicyCompliance
& compliance
) {
244 uint64_t certIssuanceTime
= GetEffectiveCertIssuanceTime(verifiedScts
);
247 CheckOperatorDiversityCompliance(verifiedScts
, certIssuanceTime
,
248 dependentOperators
, diversityOK
);
250 bool nonEmbeddedCaseOK
;
251 CheckNonEmbeddedCompliance(verifiedScts
, nonEmbeddedCaseOK
);
254 CheckEmbeddedCompliance(verifiedScts
, certLifetimeInCalendarMonths
,
255 certIssuanceTime
, embeddedCaseOK
);
257 if (nonEmbeddedCaseOK
|| embeddedCaseOK
) {
258 compliance
= diversityOK
? CTPolicyCompliance::Compliant
259 : CTPolicyCompliance::NotDiverseScts
;
261 // Not enough SCTs are present to satisfy either case of the policy.
262 compliance
= CTPolicyCompliance::NotEnoughScts
;
265 switch (compliance
) {
266 case CTPolicyCompliance::Compliant
:
267 case CTPolicyCompliance::NotEnoughScts
:
268 case CTPolicyCompliance::NotDiverseScts
:
270 case CTPolicyCompliance::Unknown
:
277 } // namespace mozilla