• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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