1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/cert/multi_log_ct_verifier.h"
6
7 #include <vector>
8
9 #include "base/functional/bind.h"
10 #include "base/functional/callback.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/values.h"
14 #include "net/base/net_errors.h"
15 #include "net/cert/ct_log_verifier.h"
16 #include "net/cert/ct_objects_extractor.h"
17 #include "net/cert/ct_serialization.h"
18 #include "net/cert/ct_signed_certificate_timestamp_log_param.h"
19 #include "net/cert/sct_status_flags.h"
20 #include "net/cert/signed_certificate_timestamp_and_status.h"
21 #include "net/cert/x509_certificate.h"
22 #include "net/log/net_log_event_type.h"
23 #include "net/log/net_log_with_source.h"
24
25 namespace net {
26
27 namespace {
28
29 // Record SCT verification status. This metric would help detecting presence
30 // of unknown CT logs as well as bad deployments (invalid SCTs).
LogSCTStatusToUMA(ct::SCTVerifyStatus status)31 void LogSCTStatusToUMA(ct::SCTVerifyStatus status) {
32 // Note SCT_STATUS_MAX + 1 is passed to the UMA_HISTOGRAM_ENUMERATION as that
33 // macro requires the values to be strictly less than the boundary value,
34 // and SCT_STATUS_MAX is the last valid value of the SCTVerifyStatus enum
35 // (since that enum is used for IPC as well).
36 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTStatus", status,
37 ct::SCT_STATUS_MAX + 1);
38 }
39
40 // Record SCT origin enum. This metric measure the popularity
41 // of the various channels of providing SCTs for a certificate.
LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin)42 void LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin) {
43 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin",
44 origin,
45 ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX);
46 }
47
AddSCTAndLogStatus(scoped_refptr<ct::SignedCertificateTimestamp> sct,ct::SCTVerifyStatus status,SignedCertificateTimestampAndStatusList * sct_list)48 void AddSCTAndLogStatus(scoped_refptr<ct::SignedCertificateTimestamp> sct,
49 ct::SCTVerifyStatus status,
50 SignedCertificateTimestampAndStatusList* sct_list) {
51 LogSCTStatusToUMA(status);
52 sct_list->push_back(SignedCertificateTimestampAndStatus(sct, status));
53 }
54
55 } // namespace
56
57 base::CallbackListSubscription
RegisterLogsListCallback(LogListCallbackList::CallbackType callback)58 MultiLogCTVerifier::CTLogProvider::RegisterLogsListCallback(
59 LogListCallbackList::CallbackType callback) {
60 return callback_list_.Add(std::move(callback));
61 }
62
NotifyCallbacks(const std::vector<scoped_refptr<const net::CTLogVerifier>> & log_verifiers)63 void MultiLogCTVerifier::CTLogProvider::NotifyCallbacks(
64 const std::vector<scoped_refptr<const net::CTLogVerifier>>& log_verifiers) {
65 callback_list_.Notify(log_verifiers);
66 }
67
68 MultiLogCTVerifier::CTLogProvider::CTLogProvider() = default;
69 MultiLogCTVerifier::CTLogProvider::~CTLogProvider() = default;
70
MultiLogCTVerifier(CTLogProvider * notifier)71 MultiLogCTVerifier::MultiLogCTVerifier(CTLogProvider* notifier) {
72 // base::Unretained is safe since we are using a CallbackListSubscription that
73 // won't outlive |this|.
74 log_provider_subscription_ =
75 notifier->RegisterLogsListCallback(base::BindRepeating(
76 &MultiLogCTVerifier::SetLogs, base::Unretained(this)));
77 }
78
79 MultiLogCTVerifier::~MultiLogCTVerifier() = default;
80
SetLogs(const std::vector<scoped_refptr<const CTLogVerifier>> & log_verifiers)81 void MultiLogCTVerifier::SetLogs(
82 const std::vector<scoped_refptr<const CTLogVerifier>>& log_verifiers) {
83 logs_.clear();
84 for (const auto& log_verifier : log_verifiers) {
85 std::string key_id = log_verifier->key_id();
86 logs_[key_id] = log_verifier;
87 }
88 }
89
Verify(base::StringPiece hostname,X509Certificate * cert,base::StringPiece stapled_ocsp_response,base::StringPiece sct_list_from_tls_extension,SignedCertificateTimestampAndStatusList * output_scts,const NetLogWithSource & net_log)90 void MultiLogCTVerifier::Verify(
91 base::StringPiece hostname,
92 X509Certificate* cert,
93 base::StringPiece stapled_ocsp_response,
94 base::StringPiece sct_list_from_tls_extension,
95 SignedCertificateTimestampAndStatusList* output_scts,
96 const NetLogWithSource& net_log) {
97 DCHECK(cert);
98 DCHECK(output_scts);
99
100 base::TimeTicks start = base::TimeTicks::Now();
101
102 output_scts->clear();
103
104 std::string embedded_scts;
105 if (!cert->intermediate_buffers().empty() &&
106 ct::ExtractEmbeddedSCTList(cert->cert_buffer(), &embedded_scts)) {
107 ct::SignedEntryData precert_entry;
108
109 if (ct::GetPrecertSignedEntry(cert->cert_buffer(),
110 cert->intermediate_buffers().front().get(),
111 &precert_entry)) {
112 VerifySCTs(hostname, embedded_scts, precert_entry,
113 ct::SignedCertificateTimestamp::SCT_EMBEDDED, cert,
114 output_scts);
115 }
116 }
117
118 std::string sct_list_from_ocsp;
119 if (!stapled_ocsp_response.empty() && !cert->intermediate_buffers().empty()) {
120 ct::ExtractSCTListFromOCSPResponse(
121 cert->intermediate_buffers().front().get(), cert->serial_number(),
122 stapled_ocsp_response, &sct_list_from_ocsp);
123 }
124
125 // Log to Net Log, after extracting SCTs but before possibly failing on
126 // X.509 entry creation.
127 net_log.AddEvent(
128 NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED, [&] {
129 return NetLogRawSignedCertificateTimestampParams(
130 embedded_scts, sct_list_from_ocsp, sct_list_from_tls_extension);
131 });
132
133 ct::SignedEntryData x509_entry;
134 if (ct::GetX509SignedEntry(cert->cert_buffer(), &x509_entry)) {
135 VerifySCTs(hostname, sct_list_from_ocsp, x509_entry,
136 ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE, cert,
137 output_scts);
138
139 VerifySCTs(hostname, sct_list_from_tls_extension, x509_entry,
140 ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, cert,
141 output_scts);
142 }
143
144 // Only log the verification time if SCTs were provided.
145 if (!output_scts->empty()) {
146 base::TimeDelta verify_time = base::TimeTicks::Now() - start;
147 UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
148 "Net.CertificateTransparency.SCT.VerificationTime", verify_time,
149 base::Microseconds(1), base::Milliseconds(100), 50);
150 }
151
152 net_log.AddEvent(NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED, [&] {
153 return NetLogSignedCertificateTimestampParams(output_scts);
154 });
155 }
156
VerifySCTs(base::StringPiece hostname,base::StringPiece encoded_sct_list,const ct::SignedEntryData & expected_entry,ct::SignedCertificateTimestamp::Origin origin,X509Certificate * cert,SignedCertificateTimestampAndStatusList * output_scts)157 void MultiLogCTVerifier::VerifySCTs(
158 base::StringPiece hostname,
159 base::StringPiece encoded_sct_list,
160 const ct::SignedEntryData& expected_entry,
161 ct::SignedCertificateTimestamp::Origin origin,
162 X509Certificate* cert,
163 SignedCertificateTimestampAndStatusList* output_scts) {
164 if (logs_.empty())
165 return;
166
167 std::vector<base::StringPiece> sct_list;
168
169 if (!ct::DecodeSCTList(encoded_sct_list, &sct_list))
170 return;
171
172 for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin();
173 it != sct_list.end(); ++it) {
174 base::StringPiece encoded_sct(*it);
175 LogSCTOriginToUMA(origin);
176
177 scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct;
178 if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) {
179 LogSCTStatusToUMA(ct::SCT_STATUS_NONE);
180 continue;
181 }
182 decoded_sct->origin = origin;
183
184 VerifySingleSCT(hostname, decoded_sct, expected_entry, cert, output_scts);
185 }
186 }
187
VerifySingleSCT(base::StringPiece hostname,scoped_refptr<ct::SignedCertificateTimestamp> sct,const ct::SignedEntryData & expected_entry,X509Certificate * cert,SignedCertificateTimestampAndStatusList * output_scts)188 bool MultiLogCTVerifier::VerifySingleSCT(
189 base::StringPiece hostname,
190 scoped_refptr<ct::SignedCertificateTimestamp> sct,
191 const ct::SignedEntryData& expected_entry,
192 X509Certificate* cert,
193 SignedCertificateTimestampAndStatusList* output_scts) {
194 // Assume this SCT is untrusted until proven otherwise.
195 const auto& it = logs_.find(sct->log_id);
196 if (it == logs_.end()) {
197 AddSCTAndLogStatus(sct, ct::SCT_STATUS_LOG_UNKNOWN, output_scts);
198 return false;
199 }
200
201 sct->log_description = it->second->description();
202
203 if (!it->second->Verify(expected_entry, *sct.get())) {
204 AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_SIGNATURE, output_scts);
205 return false;
206 }
207
208 // SCT verified ok, just make sure the timestamp is legitimate.
209 if (sct->timestamp > base::Time::Now()) {
210 AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_TIMESTAMP, output_scts);
211 return false;
212 }
213
214 AddSCTAndLogStatus(sct, ct::SCT_STATUS_OK, output_scts);
215 return true;
216 }
217
218 } // namespace net
219