Bug 1853320 [wpt PR 41940] - [FLEDGE] Split up runs of /trusted-scoring-signals.https...
[gecko.git] / security / ct / MultiLogCTVerifier.cpp
blob78f7abb7a578f3f73eadbabe8639ebcc4b65649a
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 "MultiLogCTVerifier.h"
9 #include "CTObjectsExtractor.h"
10 #include "CTSerialization.h"
12 namespace mozilla {
13 namespace ct {
15 using namespace mozilla::pkix;
17 // Note: this moves |verifiedSct| to the target list in |result|.
18 static void StoreVerifiedSct(CTVerifyResult& result, VerifiedSCT&& verifiedSct,
19 VerifiedSCT::Status status) {
20 verifiedSct.status = status;
21 result.verifiedScts.push_back(std::move(verifiedSct));
24 void MultiLogCTVerifier::AddLog(CTLogVerifier&& log) {
25 mLogs.push_back(std::move(log));
28 Result MultiLogCTVerifier::Verify(Input cert, Input issuerSubjectPublicKeyInfo,
29 Input sctListFromCert,
30 Input sctListFromOCSPResponse,
31 Input sctListFromTLSExtension, Time time,
32 CTVerifyResult& result) {
33 assert(cert.GetLength() > 0);
34 result.Reset();
36 Result rv;
38 // Verify embedded SCTs
39 if (issuerSubjectPublicKeyInfo.GetLength() > 0 &&
40 sctListFromCert.GetLength() > 0) {
41 LogEntry precertEntry;
42 rv = GetPrecertLogEntry(cert, issuerSubjectPublicKeyInfo, precertEntry);
43 if (rv != Success) {
44 return rv;
46 rv = VerifySCTs(sctListFromCert, precertEntry,
47 VerifiedSCT::Origin::Embedded, time, result);
48 if (rv != Success) {
49 return rv;
53 LogEntry x509Entry;
54 GetX509LogEntry(cert, x509Entry);
56 // Verify SCTs from a stapled OCSP response
57 if (sctListFromOCSPResponse.GetLength() > 0) {
58 rv = VerifySCTs(sctListFromOCSPResponse, x509Entry,
59 VerifiedSCT::Origin::OCSPResponse, time, result);
60 if (rv != Success) {
61 return rv;
65 // Verify SCTs from a TLS extension
66 if (sctListFromTLSExtension.GetLength() > 0) {
67 rv = VerifySCTs(sctListFromTLSExtension, x509Entry,
68 VerifiedSCT::Origin::TLSExtension, time, result);
69 if (rv != Success) {
70 return rv;
73 return Success;
76 void DecodeSCTs(Input encodedSctList,
77 std::vector<SignedCertificateTimestamp>& decodedSCTs,
78 size_t& decodingErrors) {
79 decodedSCTs.clear();
81 Reader listReader;
82 Result rv = DecodeSCTList(encodedSctList, listReader);
83 if (rv != Success) {
84 decodingErrors++;
85 return;
88 while (!listReader.AtEnd()) {
89 Input encodedSct;
90 rv = ReadSCTListItem(listReader, encodedSct);
91 if (rv != Success) {
92 decodingErrors++;
93 return;
96 Reader encodedSctReader(encodedSct);
97 SignedCertificateTimestamp sct;
98 rv = DecodeSignedCertificateTimestamp(encodedSctReader, sct);
99 if (rv != Success) {
100 decodingErrors++;
101 continue;
103 decodedSCTs.push_back(std::move(sct));
107 Result MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
108 const LogEntry& expectedEntry,
109 VerifiedSCT::Origin origin, Time time,
110 CTVerifyResult& result) {
111 std::vector<SignedCertificateTimestamp> decodedSCTs;
112 DecodeSCTs(encodedSctList, decodedSCTs, result.decodingErrors);
113 for (auto sct : decodedSCTs) {
114 Result rv =
115 VerifySingleSCT(std::move(sct), expectedEntry, origin, time, result);
116 if (rv != Success) {
117 return rv;
120 return Success;
123 Result MultiLogCTVerifier::VerifySingleSCT(SignedCertificateTimestamp&& sct,
124 const LogEntry& expectedEntry,
125 VerifiedSCT::Origin origin,
126 Time time, CTVerifyResult& result) {
127 VerifiedSCT verifiedSct;
128 verifiedSct.origin = origin;
129 verifiedSct.sct = std::move(sct);
131 CTLogVerifier* matchingLog = nullptr;
132 for (auto& log : mLogs) {
133 if (log.keyId() == verifiedSct.sct.logId) {
134 matchingLog = &log;
135 break;
139 if (!matchingLog) {
140 // SCT does not match any known log.
141 StoreVerifiedSct(result, std::move(verifiedSct),
142 VerifiedSCT::Status::UnknownLog);
143 return Success;
146 verifiedSct.logOperatorId = matchingLog->operatorId();
148 if (!matchingLog->SignatureParametersMatch(verifiedSct.sct.signature)) {
149 // SCT signature parameters do not match the log's.
150 StoreVerifiedSct(result, std::move(verifiedSct),
151 VerifiedSCT::Status::InvalidSignature);
152 return Success;
155 Result rv = matchingLog->Verify(expectedEntry, verifiedSct.sct);
156 if (rv != Success) {
157 if (rv == Result::ERROR_BAD_SIGNATURE) {
158 StoreVerifiedSct(result, std::move(verifiedSct),
159 VerifiedSCT::Status::InvalidSignature);
160 return Success;
162 return rv;
165 // Make sure the timestamp is legitimate (not in the future).
166 // SCT's |timestamp| is measured in milliseconds since the epoch,
167 // ignoring leap seconds. When converting it to a second-level precision
168 // pkix::Time, we need to round it either up or down. In our case, rounding up
169 // (towards the future) is more "secure", although practically
170 // it does not matter.
171 Time sctTime =
172 TimeFromEpochInSeconds((verifiedSct.sct.timestamp + 999u) / 1000u);
173 if (sctTime > time) {
174 StoreVerifiedSct(result, std::move(verifiedSct),
175 VerifiedSCT::Status::InvalidTimestamp);
176 return Success;
179 // SCT verified ok, see if the log is qualified. Since SCTs from
180 // disqualified logs are treated as valid under certain circumstances (see
181 // the CT Policy), the log qualification check must be the last one we do.
182 if (matchingLog->isDisqualified()) {
183 verifiedSct.logDisqualificationTime = matchingLog->disqualificationTime();
184 StoreVerifiedSct(result, std::move(verifiedSct),
185 VerifiedSCT::Status::ValidFromDisqualifiedLog);
186 return Success;
189 StoreVerifiedSct(result, std::move(verifiedSct), VerifiedSCT::Status::Valid);
190 return Success;
193 } // namespace ct
194 } // namespace mozilla