Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / security / ct / MultiLogCTVerifier.cpp
blobbed6f32a006b84209932fe17d168b4e177b2c53d
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"
11 #include "mozilla/StaticPrefs_security.h"
13 namespace mozilla {
14 namespace ct {
16 using namespace mozilla::pkix;
18 MultiLogCTVerifier::MultiLogCTVerifier()
19 : mSignatureCache(signature_cache_new(
20 StaticPrefs::security_pki_sct_signature_cache_size()),
21 signature_cache_free) {}
23 void MultiLogCTVerifier::AddLog(CTLogVerifier&& log) {
24 mLogs.push_back(std::move(log));
27 pkix::Result MultiLogCTVerifier::Verify(Input cert,
28 Input issuerSubjectPublicKeyInfo,
29 Input sctListFromCert,
30 Input sctListFromOCSPResponse,
31 Input sctListFromTLSExtension,
32 Time time, CTVerifyResult& result) {
33 assert(cert.GetLength() > 0);
34 result.Reset();
36 pkix::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, SCTOrigin::Embedded, time,
47 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, SCTOrigin::OCSPResponse,
59 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, SCTOrigin::TLSExtension,
68 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 pkix::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 pkix::Result MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
108 const LogEntry& expectedEntry,
109 SCTOrigin origin, Time time,
110 CTVerifyResult& result) {
111 std::vector<SignedCertificateTimestamp> decodedSCTs;
112 DecodeSCTs(encodedSctList, decodedSCTs, result.decodingErrors);
113 for (auto sct : decodedSCTs) {
114 pkix::Result rv =
115 VerifySingleSCT(std::move(sct), expectedEntry, origin, time, result);
116 if (rv != Success) {
117 return rv;
120 return Success;
123 pkix::Result MultiLogCTVerifier::VerifySingleSCT(
124 SignedCertificateTimestamp&& sct, const LogEntry& expectedEntry,
125 SCTOrigin origin, Time time, CTVerifyResult& result) {
126 switch (origin) {
127 case SCTOrigin::Embedded:
128 result.embeddedSCTs++;
129 break;
130 case SCTOrigin::TLSExtension:
131 result.sctsFromTLSHandshake++;
132 break;
133 case SCTOrigin::OCSPResponse:
134 result.sctsFromOCSP++;
135 break;
138 CTLogVerifier* matchingLog = nullptr;
139 for (auto& log : mLogs) {
140 if (log.keyId() == sct.logId) {
141 matchingLog = &log;
142 break;
146 if (!matchingLog) {
147 // SCT does not match any known log.
148 result.sctsFromUnknownLogs++;
149 return Success;
152 if (!matchingLog->SignatureParametersMatch(sct.signature)) {
153 // SCT signature parameters do not match the log's.
154 result.sctsWithInvalidSignatures++;
155 return Success;
158 pkix::Result rv =
159 matchingLog->Verify(expectedEntry, sct, mSignatureCache.get());
160 if (rv != Success) {
161 if (rv == pkix::Result::ERROR_BAD_SIGNATURE) {
162 result.sctsWithInvalidSignatures++;
163 return Success;
165 return rv;
168 // Make sure the timestamp is legitimate (not in the future).
169 // SCT's |timestamp| is measured in milliseconds since the epoch,
170 // ignoring leap seconds. When converting it to a second-level precision
171 // pkix::Time, we need to round it either up or down. In our case, rounding up
172 // (towards the future) is more "secure", although practically
173 // it does not matter.
174 Time sctTime = TimeFromEpochInSeconds((sct.timestamp + 999u) / 1000u);
175 if (sctTime > time) {
176 result.sctsWithInvalidTimestamps++;
177 return Success;
180 VerifiedSCT verifiedSct(std::move(sct), origin, matchingLog->operatorId(),
181 matchingLog->state(), matchingLog->timestamp());
182 result.verifiedScts.push_back(std::move(verifiedSct));
183 return Success;
186 } // namespace ct
187 } // namespace mozilla