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"
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);
38 // Verify embedded SCTs
39 if (issuerSubjectPublicKeyInfo
.GetLength() > 0 &&
40 sctListFromCert
.GetLength() > 0) {
41 LogEntry precertEntry
;
42 rv
= GetPrecertLogEntry(cert
, issuerSubjectPublicKeyInfo
, precertEntry
);
46 rv
= VerifySCTs(sctListFromCert
, precertEntry
,
47 VerifiedSCT::Origin::Embedded
, time
, result
);
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
);
65 // Verify SCTs from a TLS extension
66 if (sctListFromTLSExtension
.GetLength() > 0) {
67 rv
= VerifySCTs(sctListFromTLSExtension
, x509Entry
,
68 VerifiedSCT::Origin::TLSExtension
, time
, result
);
76 void DecodeSCTs(Input encodedSctList
,
77 std::vector
<SignedCertificateTimestamp
>& decodedSCTs
,
78 size_t& decodingErrors
) {
82 Result rv
= DecodeSCTList(encodedSctList
, listReader
);
88 while (!listReader
.AtEnd()) {
90 rv
= ReadSCTListItem(listReader
, encodedSct
);
96 Reader
encodedSctReader(encodedSct
);
97 SignedCertificateTimestamp sct
;
98 rv
= DecodeSignedCertificateTimestamp(encodedSctReader
, sct
);
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
) {
115 VerifySingleSCT(std::move(sct
), expectedEntry
, origin
, time
, result
);
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
) {
140 // SCT does not match any known log.
141 StoreVerifiedSct(result
, std::move(verifiedSct
),
142 VerifiedSCT::Status::UnknownLog
);
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
);
155 Result rv
= matchingLog
->Verify(expectedEntry
, verifiedSct
.sct
);
157 if (rv
== Result::ERROR_BAD_SIGNATURE
) {
158 StoreVerifiedSct(result
, std::move(verifiedSct
),
159 VerifiedSCT::Status::InvalidSignature
);
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.
172 TimeFromEpochInSeconds((verifiedSct
.sct
.timestamp
+ 999u) / 1000u);
173 if (sctTime
> time
) {
174 StoreVerifiedSct(result
, std::move(verifiedSct
),
175 VerifiedSCT::Status::InvalidTimestamp
);
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
);
189 StoreVerifiedSct(result
, std::move(verifiedSct
), VerifiedSCT::Status::Valid
);
194 } // namespace mozilla