Line data Source code
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 "MultiLogCTVerifier.h"
8 :
9 : #include "CTObjectsExtractor.h"
10 : #include "CTSerialization.h"
11 : #include "mozilla/Assertions.h"
12 : #include "mozilla/Move.h"
13 :
14 : namespace mozilla { namespace ct {
15 :
16 : using namespace mozilla::pkix;
17 :
18 : // Note: this moves |verifiedSct| to the target list in |result|.
19 : static Result
20 0 : StoreVerifiedSct(CTVerifyResult& result,
21 : VerifiedSCT&& verifiedSct,
22 : VerifiedSCT::Status status)
23 : {
24 0 : verifiedSct.status = status;
25 0 : if (!result.verifiedScts.append(Move(verifiedSct))) {
26 0 : return Result::FATAL_ERROR_NO_MEMORY;
27 : }
28 0 : return Success;
29 : }
30 :
31 : Result
32 15 : MultiLogCTVerifier::AddLog(CTLogVerifier&& log)
33 : {
34 15 : if (!mLogs.append(Move(log))) {
35 0 : return Result::FATAL_ERROR_NO_MEMORY;
36 : }
37 15 : return Success;
38 : }
39 :
40 : Result
41 0 : MultiLogCTVerifier::Verify(Input cert,
42 : Input issuerSubjectPublicKeyInfo,
43 : Input sctListFromCert,
44 : Input sctListFromOCSPResponse,
45 : Input sctListFromTLSExtension,
46 : Time time,
47 : CTVerifyResult& result)
48 : {
49 0 : MOZ_ASSERT(cert.GetLength() > 0);
50 0 : result.Reset();
51 :
52 : Result rv;
53 :
54 : // Verify embedded SCTs
55 0 : if (issuerSubjectPublicKeyInfo.GetLength() > 0 &&
56 0 : sctListFromCert.GetLength() > 0) {
57 0 : LogEntry precertEntry;
58 0 : rv = GetPrecertLogEntry(cert, issuerSubjectPublicKeyInfo, precertEntry);
59 0 : if (rv != Success) {
60 0 : return rv;
61 : }
62 : rv = VerifySCTs(sctListFromCert, precertEntry,
63 0 : VerifiedSCT::Origin::Embedded, time, result);
64 0 : if (rv != Success) {
65 0 : return rv;
66 : }
67 : }
68 :
69 0 : LogEntry x509Entry;
70 0 : rv = GetX509LogEntry(cert, x509Entry);
71 0 : if (rv != Success) {
72 0 : return rv;
73 : }
74 :
75 : // Verify SCTs from a stapled OCSP response
76 0 : if (sctListFromOCSPResponse.GetLength() > 0) {
77 : rv = VerifySCTs(sctListFromOCSPResponse, x509Entry,
78 0 : VerifiedSCT::Origin::OCSPResponse, time, result);
79 0 : if (rv != Success) {
80 0 : return rv;
81 : }
82 : }
83 :
84 : // Verify SCTs from a TLS extension
85 0 : if (sctListFromTLSExtension.GetLength() > 0) {
86 : rv = VerifySCTs(sctListFromTLSExtension, x509Entry,
87 0 : VerifiedSCT::Origin::TLSExtension, time, result);
88 0 : if (rv != Success) {
89 0 : return rv;
90 : }
91 : }
92 0 : return Success;
93 : }
94 :
95 : Result
96 0 : MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
97 : const LogEntry& expectedEntry,
98 : VerifiedSCT::Origin origin,
99 : Time time,
100 : CTVerifyResult& result)
101 : {
102 0 : Reader listReader;
103 0 : Result rv = DecodeSCTList(encodedSctList, listReader);
104 0 : if (rv != Success) {
105 0 : result.decodingErrors++;
106 0 : return Success;
107 : }
108 :
109 0 : while (!listReader.AtEnd()) {
110 0 : Input encodedSct;
111 0 : rv = ReadSCTListItem(listReader, encodedSct);
112 0 : if (rv != Success) {
113 0 : result.decodingErrors++;
114 0 : return Success;
115 : }
116 :
117 0 : Reader encodedSctReader(encodedSct);
118 0 : SignedCertificateTimestamp sct;
119 0 : rv = DecodeSignedCertificateTimestamp(encodedSctReader, sct);
120 0 : if (rv != Success) {
121 0 : result.decodingErrors++;
122 0 : continue;
123 : }
124 :
125 0 : rv = VerifySingleSCT(Move(sct), expectedEntry, origin, time, result);
126 0 : if (rv != Success) {
127 0 : return rv;
128 : }
129 : }
130 0 : return Success;
131 : }
132 :
133 : Result
134 0 : MultiLogCTVerifier::VerifySingleSCT(SignedCertificateTimestamp&& sct,
135 : const LogEntry& expectedEntry,
136 : VerifiedSCT::Origin origin,
137 : Time time,
138 : CTVerifyResult& result)
139 : {
140 0 : VerifiedSCT verifiedSct;
141 0 : verifiedSct.origin = origin;
142 0 : verifiedSct.sct = Move(sct);
143 :
144 0 : CTLogVerifier* matchingLog = nullptr;
145 0 : for (auto& log : mLogs) {
146 0 : if (log.keyId() == verifiedSct.sct.logId) {
147 0 : matchingLog = &log;
148 0 : break;
149 : }
150 : }
151 :
152 0 : if (!matchingLog) {
153 : // SCT does not match any known log.
154 0 : return StoreVerifiedSct(result, Move(verifiedSct),
155 0 : VerifiedSCT::Status::UnknownLog);
156 : }
157 :
158 0 : verifiedSct.logOperatorId = matchingLog->operatorId();
159 :
160 0 : if (!matchingLog->SignatureParametersMatch(verifiedSct.sct.signature)) {
161 : // SCT signature parameters do not match the log's.
162 0 : return StoreVerifiedSct(result, Move(verifiedSct),
163 0 : VerifiedSCT::Status::InvalidSignature);
164 : }
165 :
166 0 : Result rv = matchingLog->Verify(expectedEntry, verifiedSct.sct);
167 0 : if (rv != Success) {
168 0 : if (rv == Result::ERROR_BAD_SIGNATURE) {
169 0 : return StoreVerifiedSct(result, Move(verifiedSct),
170 0 : VerifiedSCT::Status::InvalidSignature);
171 : }
172 0 : return rv;
173 : }
174 :
175 : // Make sure the timestamp is legitimate (not in the future).
176 : // SCT's |timestamp| is measured in milliseconds since the epoch,
177 : // ignoring leap seconds. When converting it to a second-level precision
178 : // pkix::Time, we need to round it either up or down. In our case, rounding up
179 : // (towards the future) is more "secure", although practically
180 : // it does not matter.
181 : Time sctTime =
182 0 : TimeFromEpochInSeconds((verifiedSct.sct.timestamp + 999u) / 1000u);
183 0 : if (sctTime > time) {
184 0 : return StoreVerifiedSct(result, Move(verifiedSct),
185 0 : VerifiedSCT::Status::InvalidTimestamp);
186 : }
187 :
188 : // SCT verified ok, see if the log is qualified. Since SCTs from
189 : // disqualified logs are treated as valid under certain circumstances (see
190 : // the CT Policy), the log qualification check must be the last one we do.
191 0 : if (matchingLog->isDisqualified()) {
192 0 : verifiedSct.logDisqualificationTime = matchingLog->disqualificationTime();
193 0 : return StoreVerifiedSct(result, Move(verifiedSct),
194 0 : VerifiedSCT::Status::ValidFromDisqualifiedLog);
195 : }
196 :
197 0 : return StoreVerifiedSct(result, Move(verifiedSct),
198 0 : VerifiedSCT::Status::Valid);
199 : }
200 :
201 : } } // namespace mozilla::ct
|