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(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 X509Certificate* cert,
92 base::StringPiece stapled_ocsp_response,
93 base::StringPiece sct_list_from_tls_extension,
94 SignedCertificateTimestampAndStatusList* output_scts,
95 const NetLogWithSource& net_log) {
96 DCHECK(cert);
97 DCHECK(output_scts);
98
99 output_scts->clear();
100
101 std::string embedded_scts;
102 if (!cert->intermediate_buffers().empty() &&
103 ct::ExtractEmbeddedSCTList(cert->cert_buffer(), &embedded_scts)) {
104 ct::SignedEntryData precert_entry;
105
106 if (ct::GetPrecertSignedEntry(cert->cert_buffer(),
107 cert->intermediate_buffers().front().get(),
108 &precert_entry)) {
109 VerifySCTs(embedded_scts, precert_entry,
110 ct::SignedCertificateTimestamp::SCT_EMBEDDED, cert,
111 output_scts);
112 }
113 }
114
115 std::string sct_list_from_ocsp;
116 if (!stapled_ocsp_response.empty() && !cert->intermediate_buffers().empty()) {
117 ct::ExtractSCTListFromOCSPResponse(
118 cert->intermediate_buffers().front().get(), cert->serial_number(),
119 stapled_ocsp_response, &sct_list_from_ocsp);
120 }
121
122 // Log to Net Log, after extracting SCTs but before possibly failing on
123 // X.509 entry creation.
124 net_log.AddEvent(
125 NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED, [&] {
126 return NetLogRawSignedCertificateTimestampParams(
127 embedded_scts, sct_list_from_ocsp, sct_list_from_tls_extension);
128 });
129
130 ct::SignedEntryData x509_entry;
131 if (ct::GetX509SignedEntry(cert->cert_buffer(), &x509_entry)) {
132 VerifySCTs(sct_list_from_ocsp, x509_entry,
133 ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE, cert,
134 output_scts);
135
136 VerifySCTs(sct_list_from_tls_extension, x509_entry,
137 ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, cert,
138 output_scts);
139 }
140
141 net_log.AddEvent(NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED, [&] {
142 return NetLogSignedCertificateTimestampParams(output_scts);
143 });
144 }
145
VerifySCTs(base::StringPiece encoded_sct_list,const ct::SignedEntryData & expected_entry,ct::SignedCertificateTimestamp::Origin origin,X509Certificate * cert,SignedCertificateTimestampAndStatusList * output_scts)146 void MultiLogCTVerifier::VerifySCTs(
147 base::StringPiece encoded_sct_list,
148 const ct::SignedEntryData& expected_entry,
149 ct::SignedCertificateTimestamp::Origin origin,
150 X509Certificate* cert,
151 SignedCertificateTimestampAndStatusList* output_scts) {
152 if (logs_.empty())
153 return;
154
155 std::vector<base::StringPiece> sct_list;
156
157 if (!ct::DecodeSCTList(encoded_sct_list, &sct_list))
158 return;
159
160 for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin();
161 it != sct_list.end(); ++it) {
162 base::StringPiece encoded_sct(*it);
163 LogSCTOriginToUMA(origin);
164
165 scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct;
166 if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) {
167 LogSCTStatusToUMA(ct::SCT_STATUS_NONE);
168 continue;
169 }
170 decoded_sct->origin = origin;
171
172 VerifySingleSCT(decoded_sct, expected_entry, cert, output_scts);
173 }
174 }
175
VerifySingleSCT(scoped_refptr<ct::SignedCertificateTimestamp> sct,const ct::SignedEntryData & expected_entry,X509Certificate * cert,SignedCertificateTimestampAndStatusList * output_scts)176 bool MultiLogCTVerifier::VerifySingleSCT(
177 scoped_refptr<ct::SignedCertificateTimestamp> sct,
178 const ct::SignedEntryData& expected_entry,
179 X509Certificate* cert,
180 SignedCertificateTimestampAndStatusList* output_scts) {
181 // Assume this SCT is untrusted until proven otherwise.
182 const auto& it = logs_.find(sct->log_id);
183 if (it == logs_.end()) {
184 AddSCTAndLogStatus(sct, ct::SCT_STATUS_LOG_UNKNOWN, output_scts);
185 return false;
186 }
187
188 sct->log_description = it->second->description();
189
190 if (!it->second->Verify(expected_entry, *sct.get())) {
191 AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_SIGNATURE, output_scts);
192 return false;
193 }
194
195 // SCT verified ok, just make sure the timestamp is legitimate.
196 if (sct->timestamp > base::Time::Now()) {
197 AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_TIMESTAMP, output_scts);
198 return false;
199 }
200
201 AddSCTAndLogStatus(sct, ct::SCT_STATUS_OK, output_scts);
202 return true;
203 }
204
205 } // namespace net
206