• 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(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